/*-------------------------------------------------------------------------
 *
 * geo_ops.c
 *	  2D 几何操作
 *
 * 此模块实现几何函数和运算符。几何类型有（从简单到复杂）：
 *
 * - 点
 * - 线
 * - 线段
 * - 矩形
 * - 圆
 * - 多边形
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/backend/utils/adt/geo_ops.c
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include <math.h>
#include <limits.h>
#include <float.h>
#include <ctype.h>

#include "libpq/pqformat.h"
#include "miscadmin.h"
#include "utils/float.h"
#include "utils/fmgrprotos.h"
#include "utils/geo_decls.h"

/*
 * 类型构造函数的形式：
 *   void type_construct(Type *result, ...);
 *
 * 运算符通常具有以下签名：
 *   void type1_operator_type2(Type *result, Type1 *obj1, Type2 *obj2);
 *
 * 常见运算符包括：
 * * 交点：
 *   bool type1_interpt_type2(Point *result, Type1 *obj1, Type2 *obj2);
 *		返回两个对象是否相交。如果 *result 不是 NULL，
 *		则设置为交点。
 *
 * * 包含关系：
 *   bool type1_contain_type2(Type1 *obj1, Type2 *obj2);
 *		返回 obj1 是否包含 obj2。
 *   bool type1_contain_type2(Type1 *contains_obj, Type1 *contained_obj);
 *		返回 obj1 是否包含 obj2（当类型相同时使用）
 *
 * * obj1 中或在 obj2 最近点的距离：
 *   float8 type1_closept_type2(Point *result, Type1 *obj1, Type2 *obj2);
 *		返回两个对象之间的最短距离。如果 *result 不是
 *		NULL，则设置为 obj1 中或在 obj2 最近的点。
 *
 * 这些函数可以用于实现多个 SQL 级别的运算符。例如，判断
 * 两条线是否平行是通过检查它们是否不相交来实现的。
 */

/*
 * 内部例程
 */

enum path_delim
{
	PATH_NONE, PATH_OPEN, PATH_CLOSED
};

/* 点的例程 */
static inline void fc_point_construct(Point *fc_result, float8 fc_x, float8 fc_y);
static inline void fc_point_add_point(Point *fc_result, Point *fc_pt1, Point *fc_pt2);
static inline void fc_point_sub_point(Point *fc_result, Point *fc_pt1, Point *fc_pt2);
static inline void fc_point_mul_point(Point *fc_result, Point *fc_pt1, Point *fc_pt2);
static inline void fc_point_div_point(Point *fc_result, Point *fc_pt1, Point *fc_pt2);
static inline bool fc_point_eq_point(Point *fc_pt1, Point *fc_pt2);
static inline float8 fc_point_dt(Point *fc_pt1, Point *fc_pt2);
static inline float8 fc_point_sl(Point *fc_pt1, Point *fc_pt2);
static int	fc_point_inside(Point *fc_p, int fc_npts, Point *fc_plist);

/* 线的例程 */
static inline void fc_line_construct(LINE *fc_result, Point *fc_pt, float8 fc_m);
static inline float8 fc_line_sl(LINE *fc_line);
static inline float8 fc_line_invsl(LINE *fc_line);
static bool fc_line_interpt_line(Point *fc_result, LINE *fc_l1, LINE *fc_l2);
static bool fc_line_contain_point(LINE *fc_line, Point *fc_point);
static float8 fc_line_closept_point(Point *fc_result, LINE *fc_line, Point *fc_pt);

/* 线段的例程 */
static inline void fc_statlseg_construct(LSEG *fc_lseg, Point *fc_pt1, Point *fc_pt2);
static inline float8 fc_lseg_sl(LSEG *fc_lseg);
static inline float8 fc_lseg_invsl(LSEG *fc_lseg);
static bool fc_lseg_interpt_line(Point *fc_result, LSEG *fc_lseg, LINE *fc_line);
static bool fc_lseg_interpt_lseg(Point *fc_result, LSEG *fc_l1, LSEG *fc_l2);
static int	fc_lseg_crossing(float8 fc_x, float8 fc_y, float8 fc_px, float8 fc_py);
static bool fc_lseg_contain_point(LSEG *fc_lseg, Point *fc_point);
static float8 fc_lseg_closept_point(Point *fc_result, LSEG *fc_lseg, Point *fc_pt);
static float8 fc_lseg_closept_line(Point *fc_result, LSEG *fc_lseg, LINE *fc_line);
static float8 fc_lseg_closept_lseg(Point *fc_result, LSEG *fc_on_lseg, LSEG *fc_to_lseg);

/* 矩形的例程 */
static inline void fc_box_construct(BOX *fc_result, Point *fc_pt1, Point *fc_pt2);
static void fc_box_cn(Point *fc_center, BOX *fc_box);
static bool fc_box_ov(BOX *fc_box1, BOX *fc_box2);
static float8 fc_box_ar(BOX *fc_box);
static float8 fc_box_ht(BOX *fc_box);
static float8 fc_box_wd(BOX *fc_box);
static bool fc_box_contain_point(BOX *fc_box, Point *fc_point);
static bool fc_box_contain_box(BOX *fc_contains_box, BOX *fc_contained_box);
static bool fc_box_contain_lseg(BOX *fc_box, LSEG *fc_lseg);
static bool fc_box_interpt_lseg(Point *fc_result, BOX *fc_box, LSEG *fc_lseg);
static float8 fc_box_closept_point(Point *fc_result, BOX *fc_box, Point *fc_point);
static float8 fc_box_closept_lseg(Point *fc_result, BOX *fc_box, LSEG *fc_lseg);

/* 圆的例程 */
static float8 fc_circle_ar(CIRCLE *fc_circle);

/* 多边形的例程 */
static void fc_make_bound_box(POLYGON *fc_poly);
static void fc_poly_to_circle(CIRCLE *fc_result, POLYGON *fc_poly);
static bool fc_lseg_inside_poly(Point *fc_a, Point *fc_b, POLYGON *fc_poly, int fc_start);
static bool fc_poly_contain_poly(POLYGON *fc_contains_poly, POLYGON *fc_contained_poly);
static bool fc_plist_same(int fc_npts, Point *fc_p1, Point *fc_p2);
static float8 fc_dist_ppoly_internal(Point *fc_pt, POLYGON *fc_poly);

/* 编码和解码的例程 */
static float8 fc_single_decode(char *fc_num, char **fc_endptr_p,
							const char *fc_type_name, const char *fc_orig_string);
static void fc_single_encode(float8 fc_x, StringInfo fc_str);
static void fc_pair_decode(char *fc_str, float8 *fc_x, float8 *fc_y, char **fc_endptr_p,
						const char *fc_type_name, const char *fc_orig_string);
static void fc_pair_encode(float8 fc_x, float8 fc_y, StringInfo fc_str);
static int	fc_pair_count(char *fc_s, char fc_delim);
static void fc_path_decode(char *fc_str, bool fc_opentype, int fc_npts, Point *fc_p,
						bool *fc_isopen, char **fc_endptr_p,
						const char *fc_type_name, const char *fc_orig_string);
static char *fc_path_encode(enum path_delim fc_path_delim, int fc_npts, Point *fc_pt);


/*
 * 输入和输出字符串的分隔符。
 * LDELIM、RDELIM 和 DELIM 分别是左、右和分隔符。
 * LDELIM_EP、RDELIM_EP 是带有端点的路径的左右分隔符。
 */

#define LDELIM			'('
#define RDELIM			')'
#define DELIM			','
#define LDELIM_EP		'['
#define RDELIM_EP		']'
#define LDELIM_C		'<'
#define RDELIM_C		'>'
#define LDELIM_L		'{'
#define RDELIM_L		'}'


/*
 * 几何数据类型由点组成。
 * 这段代码尝试在数据类型中支持统一格式，
 * 以便于更可预测的使用和数据类型转换。
 * 基础单元是点。其他单元包括线段、
 * 开放路径、矩形、封闭路径和多边形（应视为
 * 不相交的封闭路径）。
 *
 * 数据表示如下：
 *	点：				(x,y)
 *	线段：			[(x1,y1),(x2,y2)]
 *	矩形：			(x1,y1),(x2,y2)
 *	开放路径：		[(x1,y1),...,(xn,yn)]
 *	封闭路径：		((x1,y1),...,(xn,yn))
 *	多边形：		((x1,y1),...,(xn,yn))
 *
 * 对于矩形，点是对角的角落，第一点位于右上角。
 * 对于封闭路径和多边形，点应重新排序以便
 * 快速且正确的相等比较。
 *
 * XXX 也许复杂形状中的点应该在内部重新排序
 * 以加快内部操作，但应该跟踪输入顺序
 * 并为文本输出恢复该顺序 - tgl 97/01/16
 */

static float8 fc_single_decode(char *fc_num, char **fc_endptr_p,
			  const char *fc_type_name, const char *fc_orig_string)
{
	return float8in_internal(fc_num, fc_endptr_p, fc_type_name, fc_orig_string);
}								/* single_decode() */

static void fc_single_encode(float8 fc_x, StringInfo fc_str)
{
	char	   *fc_xstr = float8out_internal(fc_x);

	appendStringInfoString(fc_str, fc_xstr);
	pfree(fc_xstr);
}								/* single_encode() */

static void fc_pair_decode(char *fc_str, float8 *fc_x, float8 *fc_y, char **fc_endptr_p,
			const char *fc_type_name, const char *fc_orig_string)
{
	bool		fc_has_delim;

	while (isspace((unsigned char) *fc_str))
		fc_str++;
	if ((fc_has_delim = (*fc_str == LDELIM)))
		fc_str++;

	*fc_x = float8in_internal(fc_str, &fc_str, fc_type_name, fc_orig_string);

	if (*fc_str++ != DELIM)
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
				 errmsg("invalid input syntax for type %s: \"%s\"",
						fc_type_name, fc_orig_string)));

	*fc_y = float8in_internal(fc_str, &fc_str, fc_type_name, fc_orig_string);

	if (fc_has_delim)
	{
		if (*fc_str++ != RDELIM)
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
					 errmsg("invalid input syntax for type %s: \"%s\"",
							fc_type_name, fc_orig_string)));
		while (isspace((unsigned char) *fc_str))
			fc_str++;
	}

	/* 如果需要报告停止点，否则如果不是字符串末尾则抱怨 */
	if (fc_endptr_p)
		*fc_endptr_p = fc_str;
	else if (*fc_str != '\0')
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
				 errmsg("invalid input syntax for type %s: \"%s\"",
						fc_type_name, fc_orig_string)));
}

static void fc_pair_encode(float8 fc_x, float8 fc_y, StringInfo fc_str)
{
	char	   *fc_xstr = float8out_internal(fc_x);
	char	   *fc_ystr = float8out_internal(fc_y);

	appendStringInfo(fc_str, "%s,%s", fc_xstr, fc_ystr);
	pfree(fc_xstr);
	pfree(fc_ystr);
}

static void fc_path_decode(char *fc_str, bool fc_opentype, int fc_npts, Point *fc_p,
			bool *fc_isopen, char **fc_endptr_p,
			const char *fc_type_name, const char *fc_orig_string)
{
	int			fc_depth = 0;
	char	   *fc_cp;
	int			fc_i;

	while (isspace((unsigned char) *fc_str))
		fc_str++;
	if ((*fc_isopen = (*fc_str == LDELIM_EP)))
	{
		/* 不允许开放分隔符？ */
		if (!fc_opentype)
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
					 errmsg("invalid input syntax for type %s: \"%s\"",
							fc_type_name, fc_orig_string)));
		fc_depth++;
		fc_str++;
	}
	else if (*fc_str == LDELIM)
	{
		fc_cp = (fc_str + 1);
		while (isspace((unsigned char) *fc_cp))
			fc_cp++;
		if (*fc_cp == LDELIM)
		{
			fc_depth++;
			fc_str = fc_cp;
		}
		else if (strrchr(fc_str, LDELIM) == fc_str)
		{
			fc_depth++;
			fc_str = fc_cp;
		}
	}

	for (fc_i = 0; fc_i < fc_npts; fc_i++)
	{
		fc_pair_decode(fc_str, &(fc_p->x), &(fc_p->y), &fc_str, fc_type_name, fc_orig_string);
		if (*fc_str == DELIM)
			fc_str++;
		fc_p++;
	}

	while (fc_depth > 0)
	{
		if (*fc_str == RDELIM || (*fc_str == RDELIM_EP && *fc_isopen && fc_depth == 1))
		{
			fc_depth--;
			fc_str++;
			while (isspace((unsigned char) *fc_str))
				fc_str++;
		}
		else
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
					 errmsg("invalid input syntax for type %s: \"%s\"",
							fc_type_name, fc_orig_string)));
	}

	/* 如果需要报告停止点，否则如果不是字符串末尾则抱怨 */
	if (fc_endptr_p)
		*fc_endptr_p = fc_str;
	else if (*fc_str != '\0')
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
				 errmsg("invalid input syntax for type %s: \"%s\"",
						fc_type_name, fc_orig_string)));
}								/* path_decode() */

static char * fc_path_encode(enum path_delim fc_path_delim, int fc_npts, Point *fc_pt)
{
	StringInfoData fc_str;
	int			fc_i;

	initStringInfo(&fc_str);

	switch (fc_path_delim)
	{
		case PATH_CLOSED:
			appendStringInfoChar(&fc_str, LDELIM);
			break;
		case PATH_OPEN:
			appendStringInfoChar(&fc_str, LDELIM_EP);
			break;
		case PATH_NONE:
			break;
	}

	for (fc_i = 0; fc_i < fc_npts; fc_i++)
	{
		if (fc_i > 0)
			appendStringInfoChar(&fc_str, DELIM);
		appendStringInfoChar(&fc_str, LDELIM);
		fc_pair_encode(fc_pt->x, fc_pt->y, &fc_str);
		appendStringInfoChar(&fc_str, RDELIM);
		fc_pt++;
	}

	switch (fc_path_delim)
	{
		case PATH_CLOSED:
			appendStringInfoChar(&fc_str, RDELIM);
			break;
		case PATH_OPEN:
			appendStringInfoChar(&fc_str, RDELIM_EP);
			break;
		case PATH_NONE:
			break;
	}

	return fc_str.data;
}								/* path_encode() */

/*-------------------------------------------------------------
 * pair_count - 计算点的数量
 * 允许以下表示法：
 * '((1,2),(3,4))'
 * '(1,3,2,4)'
 * 要求字符串中有奇数个分隔字符
 *-------------------------------------------------------------*/
static int fc_pair_count(char *fc_s, char fc_delim)
{
	int			fc_ndelim = 0;

	while ((fc_s = strchr(fc_s, fc_delim)) != NULL)
	{
		fc_ndelim++;
		fc_s++;
	}
	return (fc_ndelim % 2) ? ((fc_ndelim + 1) / 2) : -1;
}


/***********************************************************************
 **
 **		二维矩形的例程。
 **
 ***********************************************************************/

/*----------------------------------------------------------
 * 格式化和转换例程。
 *---------------------------------------------------------*/

/*		box_in	-		将字符串转换为内部形式。
 *
 *		外部格式：（矩形的两个角）
 *				"(f8, f8), (f8, f8)"
 *				也支持旧款式 "(f8, f8, f8, f8)"
 */
Datum box_in(PG_FUNCTION_ARGS)
{
	char	   *fc_str = PG_GETARG_CSTRING(0);
	BOX		   *fc_box = (BOX *) palloc(sizeof(BOX));
	bool		fc_isopen;
	float8		fc_x,
				fc_y;

	fc_path_decode(fc_str, false, 2, &(fc_box->high), &fc_isopen, NULL, "box", fc_str);

	/* 如有必要，重新排序角落... */
	if (float8_lt(fc_box->high.x, fc_box->low.x))
	{
		fc_x = fc_box->high.x;
		fc_box->high.x = fc_box->low.x;
		fc_box->low.x = fc_x;
	}
	if (float8_lt(fc_box->high.y, fc_box->low.y))
	{
		fc_y = fc_box->high.y;
		fc_box->high.y = fc_box->low.y;
		fc_box->low.y = fc_y;
	}

	PG_RETURN_BOX_P(fc_box);
}

/*		box_out -		将矩形转换为外部形式。
 */
Datum box_out(PG_FUNCTION_ARGS)
{
	BOX		   *fc_box = PG_GETARG_BOX_P(0);

	PG_RETURN_CSTRING(fc_path_encode(PATH_NONE, 2, &(fc_box->high)));
}

/*
 *		box_recv			- 将外部二进制格式转换为矩形
 */
Datum box_recv(PG_FUNCTION_ARGS)
{
	StringInfo	fc_buf = (StringInfo) PG_GETARG_POINTER(0);
	BOX		   *fc_box;
	float8		fc_x,
				fc_y;

	fc_box = (BOX *) palloc(sizeof(BOX));

	fc_box->high.x = pq_getmsgfloat8(fc_buf);
	fc_box->high.y = pq_getmsgfloat8(fc_buf);
	fc_box->low.x = pq_getmsgfloat8(fc_buf);
	fc_box->low.y = pq_getmsgfloat8(fc_buf);

	/* 如有必要，重新排序角落... */
	if (float8_lt(fc_box->high.x, fc_box->low.x))
	{
		fc_x = fc_box->high.x;
		fc_box->high.x = fc_box->low.x;
		fc_box->low.x = fc_x;
	}
	if (float8_lt(fc_box->high.y, fc_box->low.y))
	{
		fc_y = fc_box->high.y;
		fc_box->high.y = fc_box->low.y;
		fc_box->low.y = fc_y;
	}

	PG_RETURN_BOX_P(fc_box);
}

/*
 *		box_send			- 将矩形转换为二进制格式
 */
Datum box_send(PG_FUNCTION_ARGS)
{
	BOX		   *fc_box = PG_GETARG_BOX_P(0);
	StringInfoData fc_buf;

	pq_begintypsend(&fc_buf);
	pq_sendfloat8(&fc_buf, fc_box->high.x);
	pq_sendfloat8(&fc_buf, fc_box->high.y);
	pq_sendfloat8(&fc_buf, fc_box->low.x);
	pq_sendfloat8(&fc_buf, fc_box->low.y);
	PG_RETURN_BYTEA_P(pq_endtypsend(&fc_buf));
}


/*		box_construct	-		填充一个新矩形。
 */
static inline void fc_box_construct(BOX *fc_result, Point *fc_pt1, Point *fc_pt2)
{
	if (float8_gt(fc_pt1->x, fc_pt2->x))
	{
		fc_result->high.x = fc_pt1->x;
		fc_result->low.x = fc_pt2->x;
	}
	else
	{
		fc_result->high.x = fc_pt2->x;
		fc_result->low.x = fc_pt1->x;
	}
	if (float8_gt(fc_pt1->y, fc_pt2->y))
	{
		fc_result->high.y = fc_pt1->y;
		fc_result->low.y = fc_pt2->y;
	}
	else
	{
		fc_result->high.y = fc_pt2->y;
		fc_result->low.y = fc_pt1->y;
	}
}


/*----------------------------------------------------------
 *	矩形的关系运算符。
 *		<、>、<=、>= 和 == 基于矩形面积。
 *---------------------------------------------------------*/

/*********************** box_same - 两个框是否相同? ***********************/
Datum box_same(PG_FUNCTION_ARGS)
{
	BOX		   *fc_box1 = PG_GETARG_BOX_P(0);
	BOX		   *fc_box2 = PG_GETARG_BOX_P(1);

	PG_RETURN_BOOL(fc_point_eq_point(&fc_box1->high, &fc_box2->high) &&
				   fc_point_eq_point(&fc_box1->low, &fc_box2->low));
}

/*********************** box_overlap - box1 是否与 box2 重叠? ***********************/
Datum box_overlap(PG_FUNCTION_ARGS)
{
	BOX		   *fc_box1 = PG_GETARG_BOX_P(0);
	BOX		   *fc_box2 = PG_GETARG_BOX_P(1);

	PG_RETURN_BOOL(fc_box_ov(fc_box1, fc_box2));
}

static bool fc_box_ov(BOX *fc_box1, BOX *fc_box2)
{
	return (FPle(fc_box1->low.x, fc_box2->high.x) &&
			FPle(fc_box2->low.x, fc_box1->high.x) &&
			FPle(fc_box1->low.y, fc_box2->high.y) &&
			FPle(fc_box2->low.y, fc_box1->high.y));
}

/*********************** box_left - box1 是否严格在 box2 左边? ***********************/
Datum box_left(PG_FUNCTION_ARGS)
{
	BOX		   *fc_box1 = PG_GETARG_BOX_P(0);
	BOX		   *fc_box2 = PG_GETARG_BOX_P(1);

	PG_RETURN_BOOL(FPlt(fc_box1->high.x, fc_box2->low.x));
}

/*********************** box_overleft - box1 的右边缘是否在 box2 的右边缘左侧或相等? ***********************
 *
 *	对于时间范围的结束，这是“少于或等于”，
 *	当时间范围以矩形存储时。
***********************/
Datum box_overleft(PG_FUNCTION_ARGS)
{
	BOX		   *fc_box1 = PG_GETARG_BOX_P(0);
	BOX		   *fc_box2 = PG_GETARG_BOX_P(1);

	PG_RETURN_BOOL(FPle(fc_box1->high.x, fc_box2->high.x));
}

/*********************** box_right - box1 是否严格在 box2 右边? ***********************/
Datum box_right(PG_FUNCTION_ARGS)
{
	BOX		   *fc_box1 = PG_GETARG_BOX_P(0);
	BOX		   *fc_box2 = PG_GETARG_BOX_P(1);

	PG_RETURN_BOOL(FPgt(fc_box1->low.x, fc_box2->high.x));
}

/*********************** box_overright - box1 的左边缘是否在 box2 的左边缘右侧或相等? ***********************
 *
 *	对于时间范围，这是“大于或等于”，当时间范围
 *	以矩形存储时。
***********************/
Datum box_overright(PG_FUNCTION_ARGS)
{
	BOX		   *fc_box1 = PG_GETARG_BOX_P(0);
	BOX		   *fc_box2 = PG_GETARG_BOX_P(1);

	PG_RETURN_BOOL(FPge(fc_box1->low.x, fc_box2->low.x));
}

/*********************** box_below - box1 是否严格在 box2 下边? ***********************/
Datum box_below(PG_FUNCTION_ARGS)
{
	BOX		   *fc_box1 = PG_GETARG_BOX_P(0);
	BOX		   *fc_box2 = PG_GETARG_BOX_P(1);

	PG_RETURN_BOOL(FPlt(fc_box1->high.y, fc_box2->low.y));
}

/*********************** box_overbelow - box1 的上边缘是否在 box2 的上边缘下方或相等? ***********************/
Datum box_overbelow(PG_FUNCTION_ARGS)
{
	BOX		   *fc_box1 = PG_GETARG_BOX_P(0);
	BOX		   *fc_box2 = PG_GETARG_BOX_P(1);

	PG_RETURN_BOOL(FPle(fc_box1->high.y, fc_box2->high.y));
}

/*********************** box_above - box1 是否严格在 box2 上边? ***********************/
Datum box_above(PG_FUNCTION_ARGS)
{
	BOX		   *fc_box1 = PG_GETARG_BOX_P(0);
	BOX		   *fc_box2 = PG_GETARG_BOX_P(1);

	PG_RETURN_BOOL(FPgt(fc_box1->low.y, fc_box2->high.y));
}

/*********************** box_overabove - box1 的下边缘是否在 box2 的下边缘上方或相等? ***********************/
Datum box_overabove(PG_FUNCTION_ARGS)
{
	BOX		   *fc_box1 = PG_GETARG_BOX_P(0);
	BOX		   *fc_box2 = PG_GETARG_BOX_P(1);

	PG_RETURN_BOOL(FPge(fc_box1->low.y, fc_box2->low.y));
}

/*********************** box_contained - box1 是否被 box2 包含? ***********************/
Datum box_contained(PG_FUNCTION_ARGS)
{
	BOX		   *fc_box1 = PG_GETARG_BOX_P(0);
	BOX		   *fc_box2 = PG_GETARG_BOX_P(1);

	PG_RETURN_BOOL(fc_box_contain_box(fc_box2, fc_box1));
}

/*********************** box_contain - box1 是否包含 box2? ***********************/
Datum box_contain(PG_FUNCTION_ARGS)
{
	BOX		   *fc_box1 = PG_GETARG_BOX_P(0);
	BOX		   *fc_box2 = PG_GETARG_BOX_P(1);

	PG_RETURN_BOOL(fc_box_contain_box(fc_box1, fc_box2));
}

/***********************
 * 检查第二个框是否在第一个框内或其边界上
 ***********************/
static bool fc_box_contain_box(BOX *fc_contains_box, BOX *fc_contained_box)
{
	return FPge(fc_contains_box->high.x, fc_contained_box->high.x) &&
		FPle(fc_contains_box->low.x, fc_contained_box->low.x) &&
		FPge(fc_contains_box->high.y, fc_contained_box->high.y) &&
		FPle(fc_contains_box->low.y, fc_contained_box->low.y);
}


/*********************** box_positionop - box1 是否完全在 box2 之上或之下? ***********************
 *
 * box_below_eq 和 box_above_eq 是过时的版本（可能错误地）
 * 接受相等边界的情况。由于这些与 box_left 和 box_right 代码不一致，
 * 它们被弃用且在 PG 8.1 rtree 操作符类扩展中不被支持。
***********************/
Datum box_below_eq(PG_FUNCTION_ARGS)
{
	BOX		   *fc_box1 = PG_GETARG_BOX_P(0);
	BOX		   *fc_box2 = PG_GETARG_BOX_P(1);

	PG_RETURN_BOOL(FPle(fc_box1->high.y, fc_box2->low.y));
}

Datum box_above_eq(PG_FUNCTION_ARGS)
{
	BOX		   *fc_box1 = PG_GETARG_BOX_P(0);
	BOX		   *fc_box2 = PG_GETARG_BOX_P(1);

	PG_RETURN_BOOL(FPge(fc_box1->low.y, fc_box2->high.y));
}


/*********************** box_relop - area(box1) 是否在我们的精度限制内与 area(box2) 相关? ***********************/
Datum box_lt(PG_FUNCTION_ARGS)
{
	BOX		   *fc_box1 = PG_GETARG_BOX_P(0);
	BOX		   *fc_box2 = PG_GETARG_BOX_P(1);

	PG_RETURN_BOOL(FPlt(fc_box_ar(fc_box1), fc_box_ar(fc_box2)));
}

Datum box_gt(PG_FUNCTION_ARGS)
{
	BOX		   *fc_box1 = PG_GETARG_BOX_P(0);
	BOX		   *fc_box2 = PG_GETARG_BOX_P(1);

	PG_RETURN_BOOL(FPgt(fc_box_ar(fc_box1), fc_box_ar(fc_box2)));
}

Datum box_eq(PG_FUNCTION_ARGS)
{
	BOX		   *fc_box1 = PG_GETARG_BOX_P(0);
	BOX		   *fc_box2 = PG_GETARG_BOX_P(1);

	PG_RETURN_BOOL(FPeq(fc_box_ar(fc_box1), fc_box_ar(fc_box2)));
}

Datum box_le(PG_FUNCTION_ARGS)
{
	BOX		   *fc_box1 = PG_GETARG_BOX_P(0);
	BOX		   *fc_box2 = PG_GETARG_BOX_P(1);

	PG_RETURN_BOOL(FPle(fc_box_ar(fc_box1), fc_box_ar(fc_box2)));
}

Datum box_ge(PG_FUNCTION_ARGS)
{
	BOX		   *fc_box1 = PG_GETARG_BOX_P(0);
	BOX		   *fc_box2 = PG_GETARG_BOX_P(1);

	PG_RETURN_BOOL(FPge(fc_box_ar(fc_box1), fc_box_ar(fc_box2)));
}


/**************************************************
 * "算术" 操作符在框上。
 **************************************************/

/*********************** box_area - 返回框的面积。 ***********************/
Datum box_area(PG_FUNCTION_ARGS)
{
	BOX		   *fc_box = PG_GETARG_BOX_P(0);

	PG_RETURN_FLOAT8(fc_box_ar(fc_box));
}


/*********************** box_width - 返回框的宽度（水平量纲）。 ***********************/
Datum box_width(PG_FUNCTION_ARGS)
{
	BOX		   *fc_box = PG_GETARG_BOX_P(0);

	PG_RETURN_FLOAT8(fc_box_wd(fc_box));
}


/*********************** box_height - 返回框的高度（垂直量纲）。 ***********************/
Datum box_height(PG_FUNCTION_ARGS)
{
	BOX		   *fc_box = PG_GETARG_BOX_P(0);

	PG_RETURN_FLOAT8(fc_box_ht(fc_box));
}


/*********************** box_distance - 返回两个框中心点之间的距离。 ***********************/
Datum box_distance(PG_FUNCTION_ARGS)
{
	BOX		   *fc_box1 = PG_GETARG_BOX_P(0);
	BOX		   *fc_box2 = PG_GETARG_BOX_P(1);
	Point		fc_a,
				fc_b;

	fc_box_cn(&fc_a, fc_box1);
	fc_box_cn(&fc_b, fc_box2);

	PG_RETURN_FLOAT8(fc_point_dt(&fc_a, &fc_b));
}


/*********************** box_center - 返回框的中心点。 ***********************/
Datum box_center(PG_FUNCTION_ARGS)
{
	BOX		   *fc_box = PG_GETARG_BOX_P(0);
	Point	   *fc_result = (Point *) palloc(sizeof(Point));

	fc_box_cn(fc_result, fc_box);

	PG_RETURN_POINT_P(fc_result);
}


/*********************** box_ar - 返回框的面积。 ***********************/
static float8 fc_box_ar(BOX *fc_box)
{
	return float8_mul(fc_box_wd(fc_box), fc_box_ht(fc_box));
}


/*********************** box_cn - 将框的中心点存储到 *center 中。 ***********************/
static void fc_box_cn(Point *fc_center, BOX *fc_box)
{
	fc_center->x = float8_div(float8_pl(fc_box->high.x, fc_box->low.x), 2.0);
	fc_center->y = float8_div(float8_pl(fc_box->high.y, fc_box->low.y), 2.0);
}


/*********************** box_wd - 返回框的宽度（长度）（水平量纲）。 ***********************/
static float8 fc_box_wd(BOX *fc_box)
{
	return float8_mi(fc_box->high.x, fc_box->low.x);
}


/*********************** box_ht - 返回框的高度（垂直量纲）。 ***********************/
static float8 fc_box_ht(BOX *fc_box)
{
	return float8_mi(fc_box->high.y, fc_box->low.y);
}


/**************************************************
 * 奇特操作。
 **************************************************/

/*********************** box_intersect - 返回两个框的重叠部分，如果它们不相交则返回 NULL。 ***********************/
Datum box_intersect(PG_FUNCTION_ARGS)
{
	BOX		   *fc_box1 = PG_GETARG_BOX_P(0);
	BOX		   *fc_box2 = PG_GETARG_BOX_P(1);
	BOX		   *fc_result;

	if (!fc_box_ov(fc_box1, fc_box2))
		PG_RETURN_NULL();

	fc_result = (BOX *) palloc(sizeof(BOX));

	fc_result->high.x = float8_min(fc_box1->high.x, fc_box2->high.x);
	fc_result->low.x = float8_max(fc_box1->low.x, fc_box2->low.x);
	fc_result->high.y = float8_min(fc_box1->high.y, fc_box2->high.y);
	fc_result->low.y = float8_max(fc_box1->low.y, fc_box2->low.y);

	PG_RETURN_BOX_P(fc_result);
}


/*********************** box_diagonal - 返回一条线段，它恰好是“框”的正斜率对角线。 ***********************/
Datum box_diagonal(PG_FUNCTION_ARGS)
{
	BOX		   *fc_box = PG_GETARG_BOX_P(0);
	LSEG	   *fc_result = (LSEG *) palloc(sizeof(LSEG));

	fc_statlseg_construct(fc_result, &fc_box->high, &fc_box->low);

	PG_RETURN_LSEG_P(fc_result);
}

/***********************************************************************
 **
 ** 2D 线的例程。
 **
 ***********************************************************************/

static bool fc_line_decode(char *fc_s, const char *fc_str, LINE *fc_line)
{
	/* s 已经提前过了前导 '{' */
	fc_line->A = fc_single_decode(fc_s, &fc_s, "line", fc_str);
	if (*fc_s++ != DELIM)
		return false;
	fc_line->B = fc_single_decode(fc_s, &fc_s, "line", fc_str);
	if (*fc_s++ != DELIM)
		return false;
	fc_line->C = fc_single_decode(fc_s, &fc_s, "line", fc_str);
	if (*fc_s++ != RDELIM_L)
		return false;
	while (isspace((unsigned char) *fc_s))
		fc_s++;
	if (*fc_s != '\0')
		return false;
	return true;
}

Datum line_in(PG_FUNCTION_ARGS)
{
	char	   *fc_str = PG_GETARG_CSTRING(0);
	LINE	   *fc_line = (LINE *) palloc(sizeof(LINE));
	LSEG		fc_lseg;
	bool		fc_isopen;
	char	   *fc_s;

	fc_s = fc_str;
	while (isspace((unsigned char) *fc_s))
		fc_s++;
	if (*fc_s == LDELIM_L)
	{
		if (!fc_line_decode(fc_s + 1, fc_str, fc_line))
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
					 errmsg("invalid input syntax for type %s: \"%s\"",
							"line", fc_str)));
		if (FPzero(fc_line->A) && FPzero(fc_line->B))
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
					 errmsg("invalid line specification: A and B cannot both be zero")));
	}
	else
	{
		fc_path_decode(fc_s, true, 2, &fc_lseg.p[0], &fc_isopen, NULL, "line", fc_str);
		if (fc_point_eq_point(&fc_lseg.p[0], &fc_lseg.p[1]))
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
					 errmsg("invalid line specification: must be two distinct points")));
		fc_line_construct(fc_line, &fc_lseg.p[0], fc_lseg_sl(&fc_lseg));
	}

	PG_RETURN_LINE_P(fc_line);
}


Datum line_out(PG_FUNCTION_ARGS)
{
	LINE	   *fc_line = PG_GETARG_LINE_P(0);
	char	   *fc_astr = float8out_internal(fc_line->A);
	char	   *fc_bstr = float8out_internal(fc_line->B);
	char	   *fc_cstr = float8out_internal(fc_line->C);

	PG_RETURN_CSTRING(psprintf("%c%s%c%s%c%s%c", LDELIM_L, fc_astr, DELIM, fc_bstr,
							   DELIM, fc_cstr, RDELIM_L));
}

/*
 *		line_recv			- 将外部二进制格式转换为线
 */
Datum line_recv(PG_FUNCTION_ARGS)
{
	StringInfo	fc_buf = (StringInfo) PG_GETARG_POINTER(0);
	LINE	   *fc_line;

	fc_line = (LINE *) palloc(sizeof(LINE));

	fc_line->A = pq_getmsgfloat8(fc_buf);
	fc_line->B = pq_getmsgfloat8(fc_buf);
	fc_line->C = pq_getmsgfloat8(fc_buf);

	if (FPzero(fc_line->A) && FPzero(fc_line->B))
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
				 errmsg("invalid line specification: A and B cannot both be zero")));

	PG_RETURN_LINE_P(fc_line);
}

/*
 *		line_send			- 将线转换为二进制格式
 */
Datum line_send(PG_FUNCTION_ARGS)
{
	LINE	   *fc_line = PG_GETARG_LINE_P(0);
	StringInfoData fc_buf;

	pq_begintypsend(&fc_buf);
	pq_sendfloat8(&fc_buf, fc_line->A);
	pq_sendfloat8(&fc_buf, fc_line->B);
	pq_sendfloat8(&fc_buf, fc_line->C);
	PG_RETURN_BYTEA_P(pq_endtypsend(&fc_buf));
}


/*----------------------------------------------------------
 *	从一个线公式到内部的转换例程。
 *		内部形式：	Ax+By+C=0
 *---------------------------------------------------------*/

/*
 * 从点和斜率填充已经分配的LINE结构
 */
static inline void fc_line_construct(LINE *fc_result, Point *fc_pt, float8 fc_m)
{
	if (isinf(fc_m))
	{
		/* 垂直 - 使用 "x = C" */
		fc_result->A = -1.0;
		fc_result->B = 0.0;
		fc_result->C = fc_pt->x;
	}
	else if (fc_m == 0)
	{
		/* 水平 - 使用 "y = C" */
		fc_result->A = 0.0;
		fc_result->B = -1.0;
		fc_result->C = fc_pt->y;
	}
	else
	{
		/* 使用 "mx - y + yinter = 0" */
		fc_result->A = fc_m;
		fc_result->B = -1.0;
		fc_result->C = float8_mi(fc_pt->y, float8_mul(fc_m, fc_pt->x));
		/* 在某些平台上，前面的表达式往往会产生 -0 */
		if (fc_result->C == 0.0)
			fc_result->C = 0.0;
	}
}

/* line_construct_pp()
 * 两个点
 */
Datum line_construct_pp(PG_FUNCTION_ARGS)
{
	Point	   *fc_pt1 = PG_GETARG_POINT_P(0);
	Point	   *fc_pt2 = PG_GETARG_POINT_P(1);
	LINE	   *fc_result = (LINE *) palloc(sizeof(LINE));

	if (fc_point_eq_point(fc_pt1, fc_pt2))
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
				 errmsg("invalid line specification: must be two distinct points")));

	fc_line_construct(fc_result, fc_pt1, fc_point_sl(fc_pt1, fc_pt2));

	PG_RETURN_LINE_P(fc_result);
}


/*----------------------------------------------------------
 *	相对位置例程。
 *---------------------------------------------------------*/

Datum line_intersect(PG_FUNCTION_ARGS)
{
	LINE	   *fc_l1 = PG_GETARG_LINE_P(0);
	LINE	   *fc_l2 = PG_GETARG_LINE_P(1);

	PG_RETURN_BOOL(fc_line_interpt_line(NULL, fc_l1, fc_l2));
}

Datum line_parallel(PG_FUNCTION_ARGS)
{
	LINE	   *fc_l1 = PG_GETARG_LINE_P(0);
	LINE	   *fc_l2 = PG_GETARG_LINE_P(1);

	PG_RETURN_BOOL(!fc_line_interpt_line(NULL, fc_l1, fc_l2));
}

Datum line_perp(PG_FUNCTION_ARGS)
{
	LINE	   *fc_l1 = PG_GETARG_LINE_P(0);
	LINE	   *fc_l2 = PG_GETARG_LINE_P(1);

	if (FPzero(fc_l1->A))
		PG_RETURN_BOOL(FPzero(fc_l2->B));
	if (FPzero(fc_l2->A))
		PG_RETURN_BOOL(FPzero(fc_l1->B));
	if (FPzero(fc_l1->B))
		PG_RETURN_BOOL(FPzero(fc_l2->A));
	if (FPzero(fc_l2->B))
		PG_RETURN_BOOL(FPzero(fc_l1->A));

	PG_RETURN_BOOL(FPeq(float8_div(float8_mul(fc_l1->A, fc_l2->A),
								   float8_mul(fc_l1->B, fc_l2->B)), -1.0));
}

Datum line_vertical(PG_FUNCTION_ARGS)
{
	LINE	   *fc_line = PG_GETARG_LINE_P(0);

	PG_RETURN_BOOL(FPzero(fc_line->B));
}

Datum line_horizontal(PG_FUNCTION_ARGS)
{
	LINE	   *fc_line = PG_GETARG_LINE_P(0);

	PG_RETURN_BOOL(FPzero(fc_line->A));
}


/*
 * 检查两条线是否相同
 */
Datum line_eq(PG_FUNCTION_ARGS)
{
	LINE	   *fc_l1 = PG_GETARG_LINE_P(0);
	LINE	   *fc_l2 = PG_GETARG_LINE_P(1);
	float8		fc_ratio;

	/* 如果涉及任何NaN，要求严格相等 */
	if (unlikely(isnan(fc_l1->A) || isnan(fc_l1->B) || isnan(fc_l1->C) ||
				 isnan(fc_l2->A) || isnan(fc_l2->B) || isnan(fc_l2->C)))
	{
		PG_RETURN_BOOL(float8_eq(fc_l1->A, fc_l2->A) &&
					   float8_eq(fc_l1->B, fc_l2->B) &&
					   float8_eq(fc_l1->C, fc_l2->C));
	}

	/* 否则，参数成比例的线是相同的 */
	if (!FPzero(fc_l2->A))
		fc_ratio = float8_div(fc_l1->A, fc_l2->A);
	else if (!FPzero(fc_l2->B))
		fc_ratio = float8_div(fc_l1->B, fc_l2->B);
	else if (!FPzero(fc_l2->C))
		fc_ratio = float8_div(fc_l1->C, fc_l2->C);
	else
		fc_ratio = 1.0;

	PG_RETURN_BOOL(FPeq(fc_l1->A, float8_mul(fc_ratio, fc_l2->A)) &&
				   FPeq(fc_l1->B, float8_mul(fc_ratio, fc_l2->B)) &&
				   FPeq(fc_l1->C, float8_mul(fc_ratio, fc_l2->C)));
}


/*----------------------------------------------------------
 *	线算术例程。
 *---------------------------------------------------------*/

/*
 * 返回线的斜率
 */
static inline float8 fc_line_sl(LINE *fc_line)
{
	if (FPzero(fc_line->A))
		return 0.0;
	if (FPzero(fc_line->B))
		return get_float8_infinity();
	return float8_div(fc_line->A, -fc_line->B);
}


/*
 * 返回线的逆斜率
 */
static inline float8 fc_line_invsl(LINE *fc_line)
{
	if (FPzero(fc_line->A))
		return get_float8_infinity();
	if (FPzero(fc_line->B))
		return 0.0;
	return float8_div(fc_line->B, fc_line->A);
}


/* line_distance()
 * 两条线之间的距离。
 */
Datum line_distance(PG_FUNCTION_ARGS)
{
	LINE	   *fc_l1 = PG_GETARG_LINE_P(0);
	LINE	   *fc_l2 = PG_GETARG_LINE_P(1);
	float8		fc_ratio;

	if (fc_line_interpt_line(NULL, fc_l1, fc_l2))	/* 相交吗？ */
		PG_RETURN_FLOAT8(0.0);

	if (!FPzero(fc_l1->A) && !isnan(fc_l1->A) && !FPzero(fc_l2->A) && !isnan(fc_l2->A))
		fc_ratio = float8_div(fc_l1->A, fc_l2->A);
	else if (!FPzero(fc_l1->B) && !isnan(fc_l1->B) && !FPzero(fc_l2->B) && !isnan(fc_l2->B))
		fc_ratio = float8_div(fc_l1->B, fc_l2->B);
	else
		fc_ratio = 1.0;

	PG_RETURN_FLOAT8(float8_div(fabs(float8_mi(fc_l1->C,
											   float8_mul(fc_ratio, fc_l2->C))),
								HYPOT(fc_l1->A, fc_l1->B)));
}

/* line_interpt()
 * 两条线l1, l2相交的点（如果有）
 */
Datum line_interpt(PG_FUNCTION_ARGS)
{
	LINE	   *fc_l1 = PG_GETARG_LINE_P(0);
	LINE	   *fc_l2 = PG_GETARG_LINE_P(1);
	Point	   *fc_result;

	fc_result = (Point *) palloc(sizeof(Point));

	if (!fc_line_interpt_line(fc_result, fc_l1, fc_l2))
		PG_RETURN_NULL();
	PG_RETURN_POINT_P(fc_result);
}

/*
 * line_interpt的内部版本
 *
 * 返回两条线是否相交。如果*result不为NULL，它将被设置为
 * 交点。
 *
 * 注意：如果线是相同的，那么我们会发现它们是平行的
 * 并报告 "没有交点"。 这有点奇怪，但由于
 * 没有*唯一*的交点，也许这是适当的行为。
 *
 * 如果线有NaN常数，我们将返回true，交点
 * 将具有NaN坐标。 在这种情况下我们不应该返回false
 * 因为那样意味着线是平行的。
 */
static bool fc_line_interpt_line(Point *fc_result, LINE *fc_l1, LINE *fc_l2)
{
	float8		fc_x,
				fc_y;

	if (!FPzero(fc_l1->B))
	{
		if (FPeq(fc_l2->A, float8_mul(fc_l1->A, float8_div(fc_l2->B, fc_l1->B))))
			return false;

		fc_x = float8_div(float8_mi(float8_mul(fc_l1->B, fc_l2->C),
								 float8_mul(fc_l2->B, fc_l1->C)),
					   float8_mi(float8_mul(fc_l1->A, fc_l2->B),
								 float8_mul(fc_l2->A, fc_l1->B)));
		fc_y = float8_div(-float8_pl(float8_mul(fc_l1->A, fc_x), fc_l1->C), fc_l1->B);
	}
	else if (!FPzero(fc_l2->B))
	{
		if (FPeq(fc_l1->A, float8_mul(fc_l2->A, float8_div(fc_l1->B, fc_l2->B))))
			return false;

		fc_x = float8_div(float8_mi(float8_mul(fc_l2->B, fc_l1->C),
								 float8_mul(fc_l1->B, fc_l2->C)),
					   float8_mi(float8_mul(fc_l2->A, fc_l1->B),
								 float8_mul(fc_l1->A, fc_l2->B)));
		fc_y = float8_div(-float8_pl(float8_mul(fc_l2->A, fc_x), fc_l2->C), fc_l2->B);
	}
	else
		return false;

	/* 在某些平台上，前面的表达式往往会产生 -0。 */
	if (fc_x == 0.0)
		fc_x = 0.0;
	if (fc_y == 0.0)
		fc_y = 0.0;

	if (fc_result != NULL)
		fc_point_construct(fc_result, fc_x, fc_y);

	return true;
}


/***********************************************************************
 **
 **		二维路径的例程（线段序列，也称为
 **				`折线'）。
 **
 **				这不是一个通用的几何路径包，
 **				当然包括多边形；这里的重点是
 **				（例如）在布线布局中的用途。
 **
 ***********************************************************************/

/*----------------------------------------------------------
 *	字符串到路径 / 路径到字符串的转换。
 *		外部格式：
 *				"((xcoord, ycoord),... )"
 *				"[(xcoord, ycoord),... ]"
 *				"(xcoord, ycoord),... "
 *				"[xcoord, ycoord,... ]"
 *		也支持旧格式：
 *				"(closed, npts, xcoord, ycoord,... )"
 *---------------------------------------------------------*/

Datum path_area(PG_FUNCTION_ARGS)
{
	PATH	   *fc_path = PG_GETARG_PATH_P(0);
	float8		fc_area = 0.0;
	int			fc_i,
				fc_j;

	if (!fc_path->closed)
		PG_RETURN_NULL();

	for (fc_i = 0; fc_i < fc_path->npts; fc_i++)
	{
		fc_j = (fc_i + 1) % fc_path->npts;
		fc_area = float8_pl(fc_area, float8_mul(fc_path->p[fc_i].x, fc_path->p[fc_j].y));
		fc_area = float8_mi(fc_area, float8_mul(fc_path->p[fc_i].y, fc_path->p[fc_j].x));
	}

	PG_RETURN_FLOAT8(float8_div(fabs(fc_area), 2.0));
}


Datum path_in(PG_FUNCTION_ARGS)
{
	char	   *fc_str = PG_GETARG_CSTRING(0);
	PATH	   *fc_path;
	bool		fc_isopen;
	char	   *fc_s;
	int			fc_npts;
	int			fc_size;
	int			fc_base_size;
	int			fc_depth = 0;

	if ((fc_npts = fc_pair_count(fc_str, ',')) <= 0)
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
				 errmsg("invalid input syntax for type %s: \"%s\"",
						"path", fc_str)));

	fc_s = fc_str;
	while (isspace((unsigned char) *fc_s))
		fc_s++;

	/* 跳过单个前导括号 */
	if ((*fc_s == LDELIM) && (strrchr(fc_s, LDELIM) == fc_s))
	{
		fc_s++;
		fc_depth++;
	}

	fc_base_size = sizeof(fc_path->p[0]) * fc_npts;
	fc_size = offsetof(PATH, p) + fc_base_size;

	/* 检查整数溢出 */
	if (fc_base_size / fc_npts != sizeof(fc_path->p[0]) || fc_size <= fc_base_size)
		ereport(ERROR,
				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
				 errmsg("too many points requested")));

	fc_path = (PATH *) palloc(fc_size);

	SET_VARSIZE(fc_path, fc_size);
	fc_path->npts = fc_npts;

	fc_path_decode(fc_s, true, fc_npts, &(fc_path->p[0]), &fc_isopen, &fc_s, "path", fc_str);

	if (fc_depth >= 1)
	{
		if (*fc_s++ != RDELIM)
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
					 errmsg("invalid input syntax for type %s: \"%s\"",
							"path", fc_str)));
		while (isspace((unsigned char) *fc_s))
			fc_s++;
	}
	if (*fc_s != '\0')
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
				 errmsg("invalid input syntax for type %s: \"%s\"",
						"path", fc_str)));

	fc_path->closed = (!fc_isopen);
	/* 防止未使用填充字节的不稳定性 */
	fc_path->dummy = 0;

	PG_RETURN_PATH_P(fc_path);
}


Datum path_out(PG_FUNCTION_ARGS)
{
	PATH	   *fc_path = PG_GETARG_PATH_P(0);

	PG_RETURN_CSTRING(fc_path_encode(fc_path->closed ? PATH_CLOSED : PATH_OPEN, fc_path->npts, fc_path->p));
}

/*
 *		path_recv			- 将外部二进制格式转换为路径
 *
 * 外部表示是闭合标志（一个布尔字节）、int32点数
 * 和点。
 */
Datum path_recv(PG_FUNCTION_ARGS)
{
	StringInfo	fc_buf = (StringInfo) PG_GETARG_POINTER(0);
	PATH	   *fc_path;
	int			fc_closed;
	int32		fc_npts;
	int32		fc_i;
	int			fc_size;

	fc_closed = pq_getmsgbyte(fc_buf);
	fc_npts = pq_getmsgint(fc_buf, sizeof(int32));
	if (fc_npts <= 0 || fc_npts >= (int32) ((INT_MAX - offsetof(PATH, p)) / sizeof(Point)))
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
				 errmsg("invalid number of points in external \"path\" value")));

	fc_size = offsetof(PATH, p) + sizeof(fc_path->p[0]) * fc_npts;
	fc_path = (PATH *) palloc(fc_size);

	SET_VARSIZE(fc_path, fc_size);
	fc_path->npts = fc_npts;
	fc_path->closed = (fc_closed ? 1 : 0);
	/* 防止未使用填充字节的不稳定性 */
	fc_path->dummy = 0;

	for (fc_i = 0; fc_i < fc_npts; fc_i++)
	{
		fc_path->p[fc_i].x = pq_getmsgfloat8(fc_buf);
		fc_path->p[fc_i].y = pq_getmsgfloat8(fc_buf);
	}

	PG_RETURN_PATH_P(fc_path);
}

/*
 *		path_send			- 将路径转换为二进制格式
 */
Datum path_send(PG_FUNCTION_ARGS)
{
	PATH	   *fc_path = PG_GETARG_PATH_P(0);
	StringInfoData fc_buf;
	int32		fc_i;

	pq_begintypsend(&fc_buf);
	pq_sendbyte(&fc_buf, fc_path->closed ? 1 : 0);
	pq_sendint32(&fc_buf, fc_path->npts);
	for (fc_i = 0; fc_i < fc_path->npts; fc_i++)
	{
		pq_sendfloat8(&fc_buf, fc_path->p[fc_i].x);
		pq_sendfloat8(&fc_buf, fc_path->p[fc_i].y);
	}
	PG_RETURN_BYTEA_P(pq_endtypsend(&fc_buf));
}


/*----------------------------------------------------------
 *	关系运算符。
 *		这些基于路径基数，
 *		尽管听起来很傻。
 *
 *		更好的关系运算符和访问方法即将到来。
 *---------------------------------------------------------*/

Datum path_n_lt(PG_FUNCTION_ARGS)
{
	PATH	   *fc_p1 = PG_GETARG_PATH_P(0);
	PATH	   *fc_p2 = PG_GETARG_PATH_P(1);

	PG_RETURN_BOOL(fc_p1->npts < fc_p2->npts);
}

Datum path_n_gt(PG_FUNCTION_ARGS)
{
	PATH	   *fc_p1 = PG_GETARG_PATH_P(0);
	PATH	   *fc_p2 = PG_GETARG_PATH_P(1);

	PG_RETURN_BOOL(fc_p1->npts > fc_p2->npts);
}

Datum path_n_eq(PG_FUNCTION_ARGS)
{
	PATH	   *fc_p1 = PG_GETARG_PATH_P(0);
	PATH	   *fc_p2 = PG_GETARG_PATH_P(1);

	PG_RETURN_BOOL(fc_p1->npts == fc_p2->npts);
}

Datum path_n_le(PG_FUNCTION_ARGS)
{
	PATH	   *fc_p1 = PG_GETARG_PATH_P(0);
	PATH	   *fc_p2 = PG_GETARG_PATH_P(1);

	PG_RETURN_BOOL(fc_p1->npts <= fc_p2->npts);
}

Datum path_n_ge(PG_FUNCTION_ARGS)
{
	PATH	   *fc_p1 = PG_GETARG_PATH_P(0);
	PATH	   *fc_p2 = PG_GETARG_PATH_P(1);

	PG_RETURN_BOOL(fc_p1->npts >= fc_p2->npts);
}

/*----------------------------------------------------------
 * 转换运算符。
 *---------------------------------------------------------*/

Datum path_isclosed(PG_FUNCTION_ARGS)
{
	PATH	   *fc_path = PG_GETARG_PATH_P(0);

	PG_RETURN_BOOL(fc_path->closed);
}

Datum path_isopen(PG_FUNCTION_ARGS)
{
	PATH	   *fc_path = PG_GETARG_PATH_P(0);

	PG_RETURN_BOOL(!fc_path->closed);
}

Datum path_npoints(PG_FUNCTION_ARGS)
{
	PATH	   *fc_path = PG_GETARG_PATH_P(0);

	PG_RETURN_INT32(fc_path->npts);
}


Datum path_close(PG_FUNCTION_ARGS)
{
	PATH	   *fc_path = PG_GETARG_PATH_P_COPY(0);

	fc_path->closed = true;

	PG_RETURN_PATH_P(fc_path);
}

Datum path_open(PG_FUNCTION_ARGS)
{
	PATH	   *fc_path = PG_GETARG_PATH_P_COPY(0);

	fc_path->closed = false;

	PG_RETURN_PATH_P(fc_path);
}


/* path_inter -
 *		是否 p1 与 p2 在任何点相交？
 *		使用边界框进行快速 (O(n)) 检查，然后进行
 *		O(n^2) 迭代边检查。
 */
Datum path_inter(PG_FUNCTION_ARGS)
{
	PATH	   *fc_p1 = PG_GETARG_PATH_P(0);
	PATH	   *fc_p2 = PG_GETARG_PATH_P(1);
	BOX			fc_b1,
				fc_b2;
	int			fc_i,
				fc_j;
	LSEG		fc_seg1,
				fc_seg2;

	Assert(fc_p1->npts > 0 && fc_p2->npts > 0);

	fc_b1.high.x = fc_b1.low.x = fc_p1->p[0].x;
	fc_b1.high.y = fc_b1.low.y = fc_p1->p[0].y;
	for (fc_i = 1; fc_i < fc_p1->npts; fc_i++)
	{
		fc_b1.high.x = float8_max(fc_p1->p[fc_i].x, fc_b1.high.x);
		fc_b1.high.y = float8_max(fc_p1->p[fc_i].y, fc_b1.high.y);
		fc_b1.low.x = float8_min(fc_p1->p[fc_i].x, fc_b1.low.x);
		fc_b1.low.y = float8_min(fc_p1->p[fc_i].y, fc_b1.low.y);
	}
	fc_b2.high.x = fc_b2.low.x = fc_p2->p[0].x;
	fc_b2.high.y = fc_b2.low.y = fc_p2->p[0].y;
	for (fc_i = 1; fc_i < fc_p2->npts; fc_i++)
	{
		fc_b2.high.x = float8_max(fc_p2->p[fc_i].x, fc_b2.high.x);
		fc_b2.high.y = float8_max(fc_p2->p[fc_i].y, fc_b2.high.y);
		fc_b2.low.x = float8_min(fc_p2->p[fc_i].x, fc_b2.low.x);
		fc_b2.low.y = float8_min(fc_p2->p[fc_i].y, fc_b2.low.y);
	}
	if (!fc_box_ov(&fc_b1, &fc_b2))
		PG_RETURN_BOOL(false);

	/* 两段线相交的逐对检查 */
	for (fc_i = 0; fc_i < fc_p1->npts; fc_i++)
	{
		int			fc_iprev;

		if (fc_i > 0)
			fc_iprev = fc_i - 1;
		else
		{
			if (!fc_p1->closed)
				continue;
			fc_iprev = fc_p1->npts - 1;	/* 包括闭合线段 */
		}

		for (fc_j = 0; fc_j < fc_p2->npts; fc_j++)
		{
			int			fc_jprev;

			if (fc_j > 0)
				fc_jprev = fc_j - 1;
			else
			{
				if (!fc_p2->closed)
					continue;
				fc_jprev = fc_p2->npts - 1;	/* 包括闭合线段 */
			}

			fc_statlseg_construct(&fc_seg1, &fc_p1->p[fc_iprev], &fc_p1->p[fc_i]);
			fc_statlseg_construct(&fc_seg2, &fc_p2->p[fc_jprev], &fc_p2->p[fc_j]);
			if (fc_lseg_interpt_lseg(NULL, &fc_seg1, &fc_seg2))
				PG_RETURN_BOOL(true);
		}
	}

	/* 如果我们跳过了，则没有两个线段相交 */
	PG_RETURN_BOOL(false);
}

/* path_distance()
 * 这本质上是在两个路径中的线段做笛卡尔乘积，并找到任意两条线段之间的最小距离
 */
Datum path_distance(PG_FUNCTION_ARGS)
{
	PATH	   *fc_p1 = PG_GETARG_PATH_P(0);
	PATH	   *fc_p2 = PG_GETARG_PATH_P(1);
	float8		fc_min = 0.0;		/* 初始化以保持编译器安静 */
	bool		fc_have_min = false;
	float8		fc_tmp;
	int			fc_i,
				fc_j;
	LSEG		fc_seg1,
				fc_seg2;

	for (fc_i = 0; fc_i < fc_p1->npts; fc_i++)
	{
		int			fc_iprev;

		if (fc_i > 0)
			fc_iprev = fc_i - 1;
		else
		{
			if (!fc_p1->closed)
				continue;
			fc_iprev = fc_p1->npts - 1;	/* 包括闭合线段 */
		}

		for (fc_j = 0; fc_j < fc_p2->npts; fc_j++)
		{
			int			fc_jprev;

			if (fc_j > 0)
				fc_jprev = fc_j - 1;
			else
			{
				if (!fc_p2->closed)
					continue;
				fc_jprev = fc_p2->npts - 1;	/* 包括闭合线段 */
			}

			fc_statlseg_construct(&fc_seg1, &fc_p1->p[fc_iprev], &fc_p1->p[fc_i]);
			fc_statlseg_construct(&fc_seg2, &fc_p2->p[fc_jprev], &fc_p2->p[fc_j]);

			fc_tmp = fc_lseg_closept_lseg(NULL, &fc_seg1, &fc_seg2);
			if (!fc_have_min || float8_lt(fc_tmp, fc_min))
			{
				fc_min = fc_tmp;
				fc_have_min = true;
			}
		}
	}

	if (!fc_have_min)
		PG_RETURN_NULL();

	PG_RETURN_FLOAT8(fc_min);
}


/*----------------------------------------------------------
 *	"算术" 操作。
 *---------------------------------------------------------*/

Datum path_length(PG_FUNCTION_ARGS)
{
	PATH	   *fc_path = PG_GETARG_PATH_P(0);
	float8		fc_result = 0.0;
	int			fc_i;

	for (fc_i = 0; fc_i < fc_path->npts; fc_i++)
	{
		int			fc_iprev;

		if (fc_i > 0)
			fc_iprev = fc_i - 1;
		else
		{
			if (!fc_path->closed)
				continue;
			fc_iprev = fc_path->npts - 1; /* 包括闭合线段 */
		}

		fc_result = float8_pl(fc_result, fc_point_dt(&fc_path->p[fc_iprev], &fc_path->p[fc_i]));
	}

	PG_RETURN_FLOAT8(fc_result);
}

/***********************************************************************
 **
 **		二维点的例程。
 **
 ***********************************************************************/

/*----------------------------------------------------------
 *	字符串与点之间的转换。
 *		外部格式：
 *				"(x,y)"
 *				"x,y"
 *---------------------------------------------------------*/

Datum point_in(PG_FUNCTION_ARGS)
{
	char	   *fc_str = PG_GETARG_CSTRING(0);
	Point	   *fc_point = (Point *) palloc(sizeof(Point));

	fc_pair_decode(fc_str, &fc_point->x, &fc_point->y, NULL, "point", fc_str);
	PG_RETURN_POINT_P(fc_point);
}

Datum point_out(PG_FUNCTION_ARGS)
{
	Point	   *fc_pt = PG_GETARG_POINT_P(0);

	PG_RETURN_CSTRING(fc_path_encode(PATH_NONE, 1, fc_pt));
}

/*
 *		point_recv			- 将外部二进制格式转换为点
 */
Datum point_recv(PG_FUNCTION_ARGS)
{
	StringInfo	fc_buf = (StringInfo) PG_GETARG_POINTER(0);
	Point	   *fc_point;

	fc_point = (Point *) palloc(sizeof(Point));
	fc_point->x = pq_getmsgfloat8(fc_buf);
	fc_point->y = pq_getmsgfloat8(fc_buf);
	PG_RETURN_POINT_P(fc_point);
}

/*
 *		point_send			- 将点转换为二进制格式
 */
Datum point_send(PG_FUNCTION_ARGS)
{
	Point	   *fc_pt = PG_GETARG_POINT_P(0);
	StringInfoData fc_buf;

	pq_begintypsend(&fc_buf);
	pq_sendfloat8(&fc_buf, fc_pt->x);
	pq_sendfloat8(&fc_buf, fc_pt->y);
	PG_RETURN_BYTEA_P(pq_endtypsend(&fc_buf));
}


/*
 * 初始化一个点
 */
static inline void fc_point_construct(Point *fc_result, float8 fc_x, float8 fc_y)
{
	fc_result->x = fc_x;
	fc_result->y = fc_y;
}


/*----------------------------------------------------------
 *	点的关系运算符。
 *		由于我们确实有坐标与给定精度相等的感觉 (point_vert, point_horiz)，
 *		其他操作必须保持这种感觉。这意味着
 *		结果在严格意义上可能是谎言（除非
 *		EPSILON = 0.0）。
 *---------------------------------------------------------*/

Datum point_left(PG_FUNCTION_ARGS)
{
	Point	   *fc_pt1 = PG_GETARG_POINT_P(0);
	Point	   *fc_pt2 = PG_GETARG_POINT_P(1);

	PG_RETURN_BOOL(FPlt(fc_pt1->x, fc_pt2->x));
}

Datum point_right(PG_FUNCTION_ARGS)
{
	Point	   *fc_pt1 = PG_GETARG_POINT_P(0);
	Point	   *fc_pt2 = PG_GETARG_POINT_P(1);

	PG_RETURN_BOOL(FPgt(fc_pt1->x, fc_pt2->x));
}

Datum point_above(PG_FUNCTION_ARGS)
{
	Point	   *fc_pt1 = PG_GETARG_POINT_P(0);
	Point	   *fc_pt2 = PG_GETARG_POINT_P(1);

	PG_RETURN_BOOL(FPgt(fc_pt1->y, fc_pt2->y));
}

Datum point_below(PG_FUNCTION_ARGS)
{
	Point	   *fc_pt1 = PG_GETARG_POINT_P(0);
	Point	   *fc_pt2 = PG_GETARG_POINT_P(1);

	PG_RETURN_BOOL(FPlt(fc_pt1->y, fc_pt2->y));
}

Datum point_vert(PG_FUNCTION_ARGS)
{
	Point	   *fc_pt1 = PG_GETARG_POINT_P(0);
	Point	   *fc_pt2 = PG_GETARG_POINT_P(1);

	PG_RETURN_BOOL(FPeq(fc_pt1->x, fc_pt2->x));
}

Datum point_horiz(PG_FUNCTION_ARGS)
{
	Point	   *fc_pt1 = PG_GETARG_POINT_P(0);
	Point	   *fc_pt2 = PG_GETARG_POINT_P(1);

	PG_RETURN_BOOL(FPeq(fc_pt1->y, fc_pt2->y));
}

Datum point_eq(PG_FUNCTION_ARGS)
{
	Point	   *fc_pt1 = PG_GETARG_POINT_P(0);
	Point	   *fc_pt2 = PG_GETARG_POINT_P(1);

	PG_RETURN_BOOL(fc_point_eq_point(fc_pt1, fc_pt2));
}

Datum point_ne(PG_FUNCTION_ARGS)
{
	Point	   *fc_pt1 = PG_GETARG_POINT_P(0);
	Point	   *fc_pt2 = PG_GETARG_POINT_P(1);

	PG_RETURN_BOOL(!fc_point_eq_point(fc_pt1, fc_pt2));
}


/*
 * 检查两个点是否相同
 */
static inline bool fc_point_eq_point(Point *fc_pt1, Point *fc_pt2)
{
	/* 如果涉及任何NaN，要求严格相等 */
	if (unlikely(isnan(fc_pt1->x) || isnan(fc_pt1->y) ||
				 isnan(fc_pt2->x) || isnan(fc_pt2->y)))
		return (float8_eq(fc_pt1->x, fc_pt2->x) && float8_eq(fc_pt1->y, fc_pt2->y));

	return (FPeq(fc_pt1->x, fc_pt2->x) && FPeq(fc_pt1->y, fc_pt2->y));
}


/*----------------------------------------------------------
 *	对点进行"算术"运算的操作符。
 *---------------------------------------------------------*/

Datum point_distance(PG_FUNCTION_ARGS)
{
	Point	   *fc_pt1 = PG_GETARG_POINT_P(0);
	Point	   *fc_pt2 = PG_GETARG_POINT_P(1);

	PG_RETURN_FLOAT8(fc_point_dt(fc_pt1, fc_pt2));
}

static inline float8 fc_point_dt(Point *fc_pt1, Point *fc_pt2)
{
	return HYPOT(float8_mi(fc_pt1->x, fc_pt2->x), float8_mi(fc_pt1->y, fc_pt2->y));
}

Datum point_slope(PG_FUNCTION_ARGS)
{
	Point	   *fc_pt1 = PG_GETARG_POINT_P(0);
	Point	   *fc_pt2 = PG_GETARG_POINT_P(1);

	PG_RETURN_FLOAT8(fc_point_sl(fc_pt1, fc_pt2));
}


/*
 * 返回两点的斜率
 *
 * 请注意，当两个点相同时，此函数返回无穷大。
 */
static inline float8 fc_point_sl(Point *fc_pt1, Point *fc_pt2)
{
	if (FPeq(fc_pt1->x, fc_pt2->x))
		return get_float8_infinity();
	if (FPeq(fc_pt1->y, fc_pt2->y))
		return 0.0;
	return float8_div(float8_mi(fc_pt1->y, fc_pt2->y), float8_mi(fc_pt1->x, fc_pt2->x));
}


/*
 * 返回两点的反斜率
 *
 * 请注意，当两个点相同时，此函数返回 0.0。
 */
static inline float8 fc_point_invsl(Point *fc_pt1, Point *fc_pt2)
{
	if (FPeq(fc_pt1->x, fc_pt2->x))
		return 0.0;
	if (FPeq(fc_pt1->y, fc_pt2->y))
		return get_float8_infinity();
	return float8_div(float8_mi(fc_pt1->x, fc_pt2->x), float8_mi(fc_pt2->y, fc_pt1->y));
}


/***********************************************************************
 **
 **		二维线段的例程。
 **
 ***********************************************************************/

/*----------------------------------------------------------
 *	字符串与线段之间的转换。
 *		外部形式： "[(x1, y1), (x2, y2)]"
 *						"(x1, y1), (x2, y2)"
 *						"x1, y1, x2, y2"
 *		闭合形式可以 "((x1, y1), (x2, y2))"
 *		(旧形式)		"(x1, y1, x2, y2)"
 *---------------------------------------------------------*/

Datum lseg_in(PG_FUNCTION_ARGS)
{
	char	   *fc_str = PG_GETARG_CSTRING(0);
	LSEG	   *fc_lseg = (LSEG *) palloc(sizeof(LSEG));
	bool		fc_isopen;

	fc_path_decode(fc_str, true, 2, &fc_lseg->p[0], &fc_isopen, NULL, "lseg", fc_str);
	PG_RETURN_LSEG_P(fc_lseg);
}


Datum lseg_out(PG_FUNCTION_ARGS)
{
	LSEG	   *fc_ls = PG_GETARG_LSEG_P(0);

	PG_RETURN_CSTRING(fc_path_encode(PATH_OPEN, 2, &fc_ls->p[0]));
}

/*
 *		lseg_recv			- 将外部二进制格式转换为线段
 */
Datum lseg_recv(PG_FUNCTION_ARGS)
{
	StringInfo	fc_buf = (StringInfo) PG_GETARG_POINTER(0);
	LSEG	   *fc_lseg;

	fc_lseg = (LSEG *) palloc(sizeof(LSEG));

	fc_lseg->p[0].x = pq_getmsgfloat8(fc_buf);
	fc_lseg->p[0].y = pq_getmsgfloat8(fc_buf);
	fc_lseg->p[1].x = pq_getmsgfloat8(fc_buf);
	fc_lseg->p[1].y = pq_getmsgfloat8(fc_buf);

	PG_RETURN_LSEG_P(fc_lseg);
}

/*
 *		lseg_send			- 将线段转换为二进制格式
 */
Datum lseg_send(PG_FUNCTION_ARGS)
{
	LSEG	   *fc_ls = PG_GETARG_LSEG_P(0);
	StringInfoData fc_buf;

	pq_begintypsend(&fc_buf);
	pq_sendfloat8(&fc_buf, fc_ls->p[0].x);
	pq_sendfloat8(&fc_buf, fc_ls->p[0].y);
	pq_sendfloat8(&fc_buf, fc_ls->p[1].x);
	pq_sendfloat8(&fc_buf, fc_ls->p[1].y);
	PG_RETURN_BYTEA_P(pq_endtypsend(&fc_buf));
}


/* lseg_construct -
 *		从两个点形成一条线段。
 */
Datum lseg_construct(PG_FUNCTION_ARGS)
{
	Point	   *fc_pt1 = PG_GETARG_POINT_P(0);
	Point	   *fc_pt2 = PG_GETARG_POINT_P(1);
	LSEG	   *fc_result = (LSEG *) palloc(sizeof(LSEG));

	fc_statlseg_construct(fc_result, fc_pt1, fc_pt2);

	PG_RETURN_LSEG_P(fc_result);
}

/* 类似于 lseg_construct，但假设空间已经分配 */
static inline void fc_statlseg_construct(LSEG *fc_lseg, Point *fc_pt1, Point *fc_pt2)
{
	fc_lseg->p[0].x = fc_pt1->x;
	fc_lseg->p[0].y = fc_pt1->y;
	fc_lseg->p[1].x = fc_pt2->x;
	fc_lseg->p[1].y = fc_pt2->y;
}


/*
 * 返回线段的斜率
 */
static inline float8 fc_lseg_sl(LSEG *fc_lseg)
{
	return fc_point_sl(&fc_lseg->p[0], &fc_lseg->p[1]);
}


/*
 * 返回线段的反斜率
 */
static inline float8 fc_lseg_invsl(LSEG *fc_lseg)
{
	return fc_point_invsl(&fc_lseg->p[0], &fc_lseg->p[1]);
}


Datum lseg_length(PG_FUNCTION_ARGS)
{
	LSEG	   *fc_lseg = PG_GETARG_LSEG_P(0);

	PG_RETURN_FLOAT8(fc_point_dt(&fc_lseg->p[0], &fc_lseg->p[1]));
}

/*----------------------------------------------------------
 *	相对位置例程。
 *---------------------------------------------------------*/

/*
 **  查找两条线的交点，并查看它是否落在
 **  两个线段上。
 */
Datum lseg_intersect(PG_FUNCTION_ARGS)
{
	LSEG	   *fc_l1 = PG_GETARG_LSEG_P(0);
	LSEG	   *fc_l2 = PG_GETARG_LSEG_P(1);

	PG_RETURN_BOOL(fc_lseg_interpt_lseg(NULL, fc_l1, fc_l2));
}


Datum lseg_parallel(PG_FUNCTION_ARGS)
{
	LSEG	   *fc_l1 = PG_GETARG_LSEG_P(0);
	LSEG	   *fc_l2 = PG_GETARG_LSEG_P(1);

	PG_RETURN_BOOL(FPeq(fc_lseg_sl(fc_l1), fc_lseg_sl(fc_l2)));
}

/*
 * 确定两条线段是否垂直。
 */
Datum lseg_perp(PG_FUNCTION_ARGS)
{
	LSEG	   *fc_l1 = PG_GETARG_LSEG_P(0);
	LSEG	   *fc_l2 = PG_GETARG_LSEG_P(1);

	PG_RETURN_BOOL(FPeq(fc_lseg_sl(fc_l1), fc_lseg_invsl(fc_l2)));
}

Datum lseg_vertical(PG_FUNCTION_ARGS)
{
	LSEG	   *fc_lseg = PG_GETARG_LSEG_P(0);

	PG_RETURN_BOOL(FPeq(fc_lseg->p[0].x, fc_lseg->p[1].x));
}

Datum lseg_horizontal(PG_FUNCTION_ARGS)
{
	LSEG	   *fc_lseg = PG_GETARG_LSEG_P(0);

	PG_RETURN_BOOL(FPeq(fc_lseg->p[0].y, fc_lseg->p[1].y));
}


Datum lseg_eq(PG_FUNCTION_ARGS)
{
	LSEG	   *fc_l1 = PG_GETARG_LSEG_P(0);
	LSEG	   *fc_l2 = PG_GETARG_LSEG_P(1);

	PG_RETURN_BOOL(fc_point_eq_point(&fc_l1->p[0], &fc_l2->p[0]) &&
				   fc_point_eq_point(&fc_l1->p[1], &fc_l2->p[1]));
}

Datum lseg_ne(PG_FUNCTION_ARGS)
{
	LSEG	   *fc_l1 = PG_GETARG_LSEG_P(0);
	LSEG	   *fc_l2 = PG_GETARG_LSEG_P(1);

	PG_RETURN_BOOL(!fc_point_eq_point(&fc_l1->p[0], &fc_l2->p[0]) ||
				   !fc_point_eq_point(&fc_l1->p[1], &fc_l2->p[1]));
}

Datum lseg_lt(PG_FUNCTION_ARGS)
{
	LSEG	   *fc_l1 = PG_GETARG_LSEG_P(0);
	LSEG	   *fc_l2 = PG_GETARG_LSEG_P(1);

	PG_RETURN_BOOL(FPlt(fc_point_dt(&fc_l1->p[0], &fc_l1->p[1]),
						fc_point_dt(&fc_l2->p[0], &fc_l2->p[1])));
}

Datum lseg_le(PG_FUNCTION_ARGS)
{
	LSEG	   *fc_l1 = PG_GETARG_LSEG_P(0);
	LSEG	   *fc_l2 = PG_GETARG_LSEG_P(1);

	PG_RETURN_BOOL(FPle(fc_point_dt(&fc_l1->p[0], &fc_l1->p[1]),
						fc_point_dt(&fc_l2->p[0], &fc_l2->p[1])));
}

Datum lseg_gt(PG_FUNCTION_ARGS)
{
	LSEG	   *fc_l1 = PG_GETARG_LSEG_P(0);
	LSEG	   *fc_l2 = PG_GETARG_LSEG_P(1);

	PG_RETURN_BOOL(FPgt(fc_point_dt(&fc_l1->p[0], &fc_l1->p[1]),
						fc_point_dt(&fc_l2->p[0], &fc_l2->p[1])));
}

Datum lseg_ge(PG_FUNCTION_ARGS)
{
	LSEG	   *fc_l1 = PG_GETARG_LSEG_P(0);
	LSEG	   *fc_l2 = PG_GETARG_LSEG_P(1);

	PG_RETURN_BOOL(FPge(fc_point_dt(&fc_l1->p[0], &fc_l1->p[1]),
						fc_point_dt(&fc_l2->p[0], &fc_l2->p[1])));
}


/*----------------------------------------------------------
 *	线算术例程。
 *---------------------------------------------------------*/

/* lseg_distance -
 *		如果两条线段不相交，则最近的
 *		点将是从一个端点到另一个
 *		线段的点。
 */
Datum lseg_distance(PG_FUNCTION_ARGS)
{
	LSEG	   *fc_l1 = PG_GETARG_LSEG_P(0);
	LSEG	   *fc_l2 = PG_GETARG_LSEG_P(1);

	PG_RETURN_FLOAT8(fc_lseg_closept_lseg(NULL, fc_l1, fc_l2));
}


Datum lseg_center(PG_FUNCTION_ARGS)
{
	LSEG	   *fc_lseg = PG_GETARG_LSEG_P(0);
	Point	   *fc_result;

	fc_result = (Point *) palloc(sizeof(Point));

	fc_result->x = float8_div(float8_pl(fc_lseg->p[0].x, fc_lseg->p[1].x), 2.0);
	fc_result->y = float8_div(float8_pl(fc_lseg->p[0].y, fc_lseg->p[1].y), 2.0);

	PG_RETURN_POINT_P(fc_result);
}


/*
 * 返回两条线段是否相交。如果 *result 不为 NULL，
 * 它将设置为交点。
 *
 * 此函数几乎完全对称，尽管它看起来并不如此。
 * 有关另一半，请参见 lseg_interpt_line()。
 */
static bool fc_lseg_interpt_lseg(Point *fc_result, LSEG *fc_l1, LSEG *fc_l2)
{
	Point		fc_interpt;
	LINE		fc_tmp;

	fc_line_construct(&fc_tmp, &fc_l2->p[0], fc_lseg_sl(fc_l2));
	if (!fc_lseg_interpt_line(&fc_interpt, fc_l1, &fc_tmp))
		return false;

	/*
	 * 如果线的交点不在 l2 内，则根本没有有效的段交点。
	 */
	if (!fc_lseg_contain_point(fc_l2, &fc_interpt))
		return false;

	if (fc_result != NULL)
		*fc_result = fc_interpt;

	return true;
}

Datum lseg_interpt(PG_FUNCTION_ARGS)
{
	LSEG	   *fc_l1 = PG_GETARG_LSEG_P(0);
	LSEG	   *fc_l2 = PG_GETARG_LSEG_P(1);
	Point	   *fc_result;

	fc_result = (Point *) palloc(sizeof(Point));

	if (!fc_lseg_interpt_lseg(fc_result, fc_l1, fc_l2))
		PG_RETURN_NULL();
	PG_RETURN_POINT_P(fc_result);
}

/***********************************************************************
 **
 **		不同类型的2D对象的位置比较例程。
 **
 ***********************************************************************/

/*---------------------------------------------------------------------
 *		dist_
 *				一个对象到另一个对象的最小距离。
 *-------------------------------------------------------------------*/

/*
 * 从点到线的距离
 */
Datum dist_pl(PG_FUNCTION_ARGS)
{
	Point	   *fc_pt = PG_GETARG_POINT_P(0);
	LINE	   *fc_line = PG_GETARG_LINE_P(1);

	PG_RETURN_FLOAT8(fc_line_closept_point(NULL, fc_line, fc_pt));
}

/*
 * 从线到点的距离
 */
Datum dist_lp(PG_FUNCTION_ARGS)
{
	LINE	   *fc_line = PG_GETARG_LINE_P(0);
	Point	   *fc_pt = PG_GETARG_POINT_P(1);

	PG_RETURN_FLOAT8(fc_line_closept_point(NULL, fc_line, fc_pt));
}

/*
 * 从点到线段的距离
 */
Datum dist_ps(PG_FUNCTION_ARGS)
{
	Point	   *fc_pt = PG_GETARG_POINT_P(0);
	LSEG	   *fc_lseg = PG_GETARG_LSEG_P(1);

	PG_RETURN_FLOAT8(fc_lseg_closept_point(NULL, fc_lseg, fc_pt));
}

/*
 * 从线段到点的距离
 */
Datum dist_sp(PG_FUNCTION_ARGS)
{
	LSEG	   *fc_lseg = PG_GETARG_LSEG_P(0);
	Point	   *fc_pt = PG_GETARG_POINT_P(1);

	PG_RETURN_FLOAT8(fc_lseg_closept_point(NULL, fc_lseg, fc_pt));
}

static float8 fc_dist_ppath_internal(Point *fc_pt, PATH *fc_path)
{
	float8		fc_result = 0.0;	/* 保持编译器安静 */
	bool		fc_have_min = false;
	float8		fc_tmp;
	int			fc_i;
	LSEG		fc_lseg;

	Assert(fc_path->npts > 0);

	/*
	 * 从点到路径的距离是点到其组成段中任何一段的最小距离。
	 */
	for (fc_i = 0; fc_i < fc_path->npts; fc_i++)
	{
		int			fc_iprev;

		if (fc_i > 0)
			fc_iprev = fc_i - 1;
		else
		{
			if (!fc_path->closed)
				continue;
			fc_iprev = fc_path->npts - 1; /* 包括封闭段 */
		}

		fc_statlseg_construct(&fc_lseg, &fc_path->p[fc_iprev], &fc_path->p[fc_i]);
		fc_tmp = fc_lseg_closept_point(NULL, &fc_lseg, fc_pt);
		if (!fc_have_min || float8_lt(fc_tmp, fc_result))
		{
			fc_result = fc_tmp;
			fc_have_min = true;
		}
	}

	return fc_result;
}

/*
 * 从点到路径的距离
 */
Datum dist_ppath(PG_FUNCTION_ARGS)
{
	Point	   *fc_pt = PG_GETARG_POINT_P(0);
	PATH	   *fc_path = PG_GETARG_PATH_P(1);

	PG_RETURN_FLOAT8(fc_dist_ppath_internal(fc_pt, fc_path));
}

/*
 * 从路径到点的距离
 */
Datum dist_pathp(PG_FUNCTION_ARGS)
{
	PATH	   *fc_path = PG_GETARG_PATH_P(0);
	Point	   *fc_pt = PG_GETARG_POINT_P(1);

	PG_RETURN_FLOAT8(fc_dist_ppath_internal(fc_pt, fc_path));
}

/*
 * 从点到盒子的距离
 */
Datum dist_pb(PG_FUNCTION_ARGS)
{
	Point	   *fc_pt = PG_GETARG_POINT_P(0);
	BOX		   *fc_box = PG_GETARG_BOX_P(1);

	PG_RETURN_FLOAT8(fc_box_closept_point(NULL, fc_box, fc_pt));
}

/*
 * 从盒子到点的距离
 */
Datum dist_bp(PG_FUNCTION_ARGS)
{
	BOX		   *fc_box = PG_GETARG_BOX_P(0);
	Point	   *fc_pt = PG_GETARG_POINT_P(1);

	PG_RETURN_FLOAT8(fc_box_closept_point(NULL, fc_box, fc_pt));
}

/*
 * 从线段到线的距离
 */
Datum dist_sl(PG_FUNCTION_ARGS)
{
	LSEG	   *fc_lseg = PG_GETARG_LSEG_P(0);
	LINE	   *fc_line = PG_GETARG_LINE_P(1);

	PG_RETURN_FLOAT8(fc_lseg_closept_line(NULL, fc_lseg, fc_line));
}

/*
 * 从线到线段的距离
 */
Datum dist_ls(PG_FUNCTION_ARGS)
{
	LINE	   *fc_line = PG_GETARG_LINE_P(0);
	LSEG	   *fc_lseg = PG_GETARG_LSEG_P(1);

	PG_RETURN_FLOAT8(fc_lseg_closept_line(NULL, fc_lseg, fc_line));
}

/*
 * 从线段到盒子的距离
 */
Datum dist_sb(PG_FUNCTION_ARGS)
{
	LSEG	   *fc_lseg = PG_GETARG_LSEG_P(0);
	BOX		   *fc_box = PG_GETARG_BOX_P(1);

	PG_RETURN_FLOAT8(fc_box_closept_lseg(NULL, fc_box, fc_lseg));
}

/*
 * 从盒子到线段的距离
 */
Datum dist_bs(PG_FUNCTION_ARGS)
{
	BOX		   *fc_box = PG_GETARG_BOX_P(0);
	LSEG	   *fc_lseg = PG_GETARG_LSEG_P(1);

	PG_RETURN_FLOAT8(fc_box_closept_lseg(NULL, fc_box, fc_lseg));
}

static float8 fc_dist_cpoly_internal(CIRCLE *fc_circle, POLYGON *fc_poly)
{
	float8		fc_result;

	/* 计算到中心的距离，然后减去半径 */
	fc_result = float8_mi(fc_dist_ppoly_internal(&fc_circle->center, fc_poly),
					   fc_circle->radius);
	if (fc_result < 0.0)
		fc_result = 0.0;

	return fc_result;
}

/*
 * 从圆到多边形的距离
 */
Datum dist_cpoly(PG_FUNCTION_ARGS)
{
	CIRCLE	   *fc_circle = PG_GETARG_CIRCLE_P(0);
	POLYGON    *fc_poly = PG_GETARG_POLYGON_P(1);

	PG_RETURN_FLOAT8(fc_dist_cpoly_internal(fc_circle, fc_poly));
}

/*
 * 从多边形到圆的距离
 */
Datum dist_polyc(PG_FUNCTION_ARGS)
{
	POLYGON    *fc_poly = PG_GETARG_POLYGON_P(0);
	CIRCLE	   *fc_circle = PG_GETARG_CIRCLE_P(1);

	PG_RETURN_FLOAT8(fc_dist_cpoly_internal(fc_circle, fc_poly));
}

/*
 * 从点到多边形的距离
 */
Datum dist_ppoly(PG_FUNCTION_ARGS)
{
	Point	   *fc_point = PG_GETARG_POINT_P(0);
	POLYGON    *fc_poly = PG_GETARG_POLYGON_P(1);

	PG_RETURN_FLOAT8(fc_dist_ppoly_internal(fc_point, fc_poly));
}

Datum dist_polyp(PG_FUNCTION_ARGS)
{
	POLYGON    *fc_poly = PG_GETARG_POLYGON_P(0);
	Point	   *fc_point = PG_GETARG_POINT_P(1);

	PG_RETURN_FLOAT8(fc_dist_ppoly_internal(fc_point, fc_poly));
}

static float8 fc_dist_ppoly_internal(Point *fc_pt, POLYGON *fc_poly)
{
	float8		fc_result;
	float8		fc_d;
	int			fc_i;
	LSEG		fc_seg;

	if (fc_point_inside(fc_pt, fc_poly->npts, fc_poly->p) != 0)
		return 0.0;

	/* 用第一点和最后一点之间的线段初始化距离 */
	fc_seg.p[0].x = fc_poly->p[0].x;
	fc_seg.p[0].y = fc_poly->p[0].y;
	fc_seg.p[1].x = fc_poly->p[fc_poly->npts - 1].x;
	fc_seg.p[1].y = fc_poly->p[fc_poly->npts - 1].y;
	fc_result = fc_lseg_closept_point(NULL, &fc_seg, fc_pt);

	/* 检查其他线段的距离 */
	for (fc_i = 0; fc_i < fc_poly->npts - 1; fc_i++)
	{
		fc_seg.p[0].x = fc_poly->p[fc_i].x;
		fc_seg.p[0].y = fc_poly->p[fc_i].y;
		fc_seg.p[1].x = fc_poly->p[fc_i + 1].x;
		fc_seg.p[1].y = fc_poly->p[fc_i + 1].y;
		fc_d = fc_lseg_closept_point(NULL, &fc_seg, fc_pt);
		if (float8_lt(fc_d, fc_result))
			fc_result = fc_d;
	}

	return fc_result;
}


/*---------------------------------------------------------------------
 *		interpt_
 *				对象的交点。
 *				我们选择忽略线和盒子之间的“交点”，因为通常有两个。
 *-------------------------------------------------------------------*/

/*
 * 返回线段是否与线相交。如果*result不为NULL，则设置为交点。
 */
static bool fc_lseg_interpt_line(Point *fc_result, LSEG *fc_lseg, LINE *fc_line)
{
	Point		fc_interpt;
	LINE		fc_tmp;

	/*
	 * 首先，我们将线段提升为线，因为我们知道如何找到
	 * 两条线的交点。如果它们没有交点，我们就完成了。
	 */
	fc_line_construct(&fc_tmp, &fc_lseg->p[0], fc_lseg_sl(fc_lseg));
	if (!fc_line_interpt_line(&fc_interpt, &fc_tmp, fc_line))
		return false;

	/*
	 * 然后，我们检查交点是否实际上在该线段上。
	 */
	if (!fc_lseg_contain_point(fc_lseg, &fc_interpt))
		return false;
	if (fc_result != NULL)
	{
		/*
		 * 如果存在交点，则显式检查端点是否匹配，因为可能存在
		 * 浮点数舍入效应导致令人烦恼的最低有效位残留。
		 */
		if (fc_point_eq_point(&fc_lseg->p[0], &fc_interpt))
			*fc_result = fc_lseg->p[0];
		else if (fc_point_eq_point(&fc_lseg->p[1], &fc_interpt))
			*fc_result = fc_lseg->p[1];
		else
			*fc_result = fc_interpt;
	}

	return true;
}

/*---------------------------------------------------------------------
 *		close_
 *			 对象之间最近接触的点。
 *-------------------------------------------------------------------*/

/*
 * 如果*result不为NULL，则设置为通过该点的线的垂线的交点。返回这两点之间的距离。
 */
static float8 fc_line_closept_point(Point *fc_result, LINE *fc_line, Point *fc_point)
{
	Point		fc_closept;
	LINE		fc_tmp;

	/*
	 * 我们投下一个垂线以找到交点。通常我们应该总是能找到，
	 * 但在存在NaN坐标的情况下可能会失败，甚至可能因为简单的
	 * 舍入问题而失败。
	 */
	fc_line_construct(&fc_tmp, fc_point, fc_line_invsl(fc_line));
	if (!fc_line_interpt_line(&fc_closept, &fc_tmp, fc_line))
	{
		if (fc_result != NULL)
			*fc_result = *fc_point;

		return get_float8_nan();
	}

	if (fc_result != NULL)
		*fc_result = fc_closept;

	return fc_point_dt(&fc_closept, fc_point);
}

Datum close_pl(PG_FUNCTION_ARGS)
{
	Point	   *fc_pt = PG_GETARG_POINT_P(0);
	LINE	   *fc_line = PG_GETARG_LINE_P(1);
	Point	   *fc_result;

	fc_result = (Point *) palloc(sizeof(Point));

	if (isnan(fc_line_closept_point(fc_result, fc_line, fc_pt)))
		PG_RETURN_NULL();

	PG_RETURN_POINT_P(fc_result);
}


/*
 * 指定点到线段的最近点。
 *
 * 如果 *result 不是 NULL，将其设置为到线段的最近点。
 * 返回这两点之间的距离。
 */
static float8 fc_lseg_closept_point(Point *fc_result, LSEG *fc_lseg, Point *fc_pt)
{
	Point		fc_closept;
	LINE		fc_tmp;

	/*
	 * 为了找到最近点，我们从该点绘制一条垂线
	 * 到线段上。
	 */
	fc_line_construct(&fc_tmp, fc_pt, fc_point_invsl(&fc_lseg->p[0], &fc_lseg->p[1]));
	fc_lseg_closept_line(&fc_closept, fc_lseg, &fc_tmp);

	if (fc_result != NULL)
		*fc_result = fc_closept;

	return fc_point_dt(&fc_closept, fc_pt);
}

Datum close_ps(PG_FUNCTION_ARGS)
{
	Point	   *fc_pt = PG_GETARG_POINT_P(0);
	LSEG	   *fc_lseg = PG_GETARG_LSEG_P(1);
	Point	   *fc_result;

	fc_result = (Point *) palloc(sizeof(Point));

	if (isnan(fc_lseg_closept_point(fc_result, fc_lseg, fc_pt)))
		PG_RETURN_NULL();

	PG_RETURN_POINT_P(fc_result);
}


/*
 * 线段到线段的最近点
 */
static float8 fc_lseg_closept_lseg(Point *fc_result, LSEG *fc_on_lseg, LSEG *fc_to_lseg)
{
	Point		fc_point;
	float8		fc_dist,
				fc_d;

	/* 首先，我们处理线段相交的情况。 */
	if (fc_lseg_interpt_lseg(fc_result, fc_on_lseg, fc_to_lseg))
		return 0.0;

	/*
	 * 然后，我们找到第二条线段的端点到最近点，
	 * 并保留最近的一个。
	 */
	fc_dist = fc_lseg_closept_point(fc_result, fc_on_lseg, &fc_to_lseg->p[0]);
	fc_d = fc_lseg_closept_point(&fc_point, fc_on_lseg, &fc_to_lseg->p[1]);
	if (float8_lt(fc_d, fc_dist))
	{
		fc_dist = fc_d;
		if (fc_result != NULL)
			*fc_result = fc_point;
	}

	/* 最近的点仍然可以是端点之一，因此我们测试它们。 */
	fc_d = fc_lseg_closept_point(NULL, fc_to_lseg, &fc_on_lseg->p[0]);
	if (float8_lt(fc_d, fc_dist))
	{
		fc_dist = fc_d;
		if (fc_result != NULL)
			*fc_result = fc_on_lseg->p[0];
	}
	fc_d = fc_lseg_closept_point(NULL, fc_to_lseg, &fc_on_lseg->p[1]);
	if (float8_lt(fc_d, fc_dist))
	{
		fc_dist = fc_d;
		if (fc_result != NULL)
			*fc_result = fc_on_lseg->p[1];
	}

	return fc_dist;
}

Datum close_lseg(PG_FUNCTION_ARGS)
{
	LSEG	   *fc_l1 = PG_GETARG_LSEG_P(0);
	LSEG	   *fc_l2 = PG_GETARG_LSEG_P(1);
	Point	   *fc_result;

	if (fc_lseg_sl(fc_l1) == fc_lseg_sl(fc_l2))
		PG_RETURN_NULL();

	fc_result = (Point *) palloc(sizeof(Point));

	if (isnan(fc_lseg_closept_lseg(fc_result, fc_l2, fc_l1)))
		PG_RETURN_NULL();

	PG_RETURN_POINT_P(fc_result);
}


/*
 * 指定点到框内或上的最近点。
 *
 * 如果 *result 不是 NULL，将其设置为到框的最近点，
 * 并返回这两点之间的距离。
 */
static float8 fc_box_closept_point(Point *fc_result, BOX *fc_box, Point *fc_pt)
{
	float8		fc_dist,
				fc_d;
	Point		fc_point,
				fc_closept;
	LSEG		fc_lseg;

	if (fc_box_contain_point(fc_box, fc_pt))
	{
		if (fc_result != NULL)
			*fc_result = *fc_pt;

		return 0.0;
	}

	/* 成对检查线段距离 */
	fc_point.x = fc_box->low.x;
	fc_point.y = fc_box->high.y;
	fc_statlseg_construct(&fc_lseg, &fc_box->low, &fc_point);
	fc_dist = fc_lseg_closept_point(fc_result, &fc_lseg, fc_pt);

	fc_statlseg_construct(&fc_lseg, &fc_box->high, &fc_point);
	fc_d = fc_lseg_closept_point(&fc_closept, &fc_lseg, fc_pt);
	if (float8_lt(fc_d, fc_dist))
	{
		fc_dist = fc_d;
		if (fc_result != NULL)
			*fc_result = fc_closept;
	}

	fc_point.x = fc_box->high.x;
	fc_point.y = fc_box->low.y;
	fc_statlseg_construct(&fc_lseg, &fc_box->low, &fc_point);
	fc_d = fc_lseg_closept_point(&fc_closept, &fc_lseg, fc_pt);
	if (float8_lt(fc_d, fc_dist))
	{
		fc_dist = fc_d;
		if (fc_result != NULL)
			*fc_result = fc_closept;
	}

	fc_statlseg_construct(&fc_lseg, &fc_box->high, &fc_point);
	fc_d = fc_lseg_closept_point(&fc_closept, &fc_lseg, fc_pt);
	if (float8_lt(fc_d, fc_dist))
	{
		fc_dist = fc_d;
		if (fc_result != NULL)
			*fc_result = fc_closept;
	}

	return fc_dist;
}

Datum close_pb(PG_FUNCTION_ARGS)
{
	Point	   *fc_pt = PG_GETARG_POINT_P(0);
	BOX		   *fc_box = PG_GETARG_BOX_P(1);
	Point	   *fc_result;

	fc_result = (Point *) palloc(sizeof(Point));

	if (isnan(fc_box_closept_point(fc_result, fc_box, fc_pt)))
		PG_RETURN_NULL();

	PG_RETURN_POINT_P(fc_result);
}

/*
 * 线段到线的最近点。
 *
 * 返回线与最近的线段点之间的距离。
 * 如果 *result 不是 NULL，则将其设置为该点。
 *
 * 注意：当线平行时，线段的一个端点是 FPeq()，
 * 在存在 NaN 或无限坐标的情况下，或者
 * 可能是由于简单的舍入问题，可能不会有单个最近点。
 * 在这些情况下，我们可能会将结果设置为第二个端点。
 */
static float8 fc_lseg_closept_line(Point *fc_result, LSEG *fc_lseg, LINE *fc_line)
{
	float8		fc_dist1,
				fc_dist2;

	if (fc_lseg_interpt_line(fc_result, fc_lseg, fc_line))
		return 0.0;

	fc_dist1 = fc_line_closept_point(NULL, fc_line, &fc_lseg->p[0]);
	fc_dist2 = fc_line_closept_point(NULL, fc_line, &fc_lseg->p[1]);

	if (fc_dist1 < fc_dist2)
	{
		if (fc_result != NULL)
			*fc_result = fc_lseg->p[0];

		return fc_dist1;
	}
	else
	{
		if (fc_result != NULL)
			*fc_result = fc_lseg->p[1];

		return fc_dist2;
	}
}

Datum close_ls(PG_FUNCTION_ARGS)
{
	LINE	   *fc_line = PG_GETARG_LINE_P(0);
	LSEG	   *fc_lseg = PG_GETARG_LSEG_P(1);
	Point	   *fc_result;

	if (fc_lseg_sl(fc_lseg) == fc_line_sl(fc_line))
		PG_RETURN_NULL();

	fc_result = (Point *) palloc(sizeof(Point));

	if (isnan(fc_lseg_closept_line(fc_result, fc_lseg, fc_line)))
		PG_RETURN_NULL();

	PG_RETURN_POINT_P(fc_result);
}


/*
 * 线段到框内或上的最近点。
 *
 * 返回到线段的最近点与框内或上的距离。
 * 如果 *result 不是 NULL，则将其设置为该点。
 */
static float8 fc_box_closept_lseg(Point *fc_result, BOX *fc_box, LSEG *fc_lseg)
{
	float8		fc_dist,
				fc_d;
	Point		fc_point,
				fc_closept;
	LSEG		fc_bseg;

	if (fc_box_interpt_lseg(fc_result, fc_box, fc_lseg))
		return 0.0;

	/* 成对检查线段距离 */
	fc_point.x = fc_box->low.x;
	fc_point.y = fc_box->high.y;
	fc_statlseg_construct(&fc_bseg, &fc_box->low, &fc_point);
	fc_dist = fc_lseg_closept_lseg(fc_result, &fc_bseg, fc_lseg);

	fc_statlseg_construct(&fc_bseg, &fc_box->high, &fc_point);
	fc_d = fc_lseg_closept_lseg(&fc_closept, &fc_bseg, fc_lseg);
	if (float8_lt(fc_d, fc_dist))
	{
		fc_dist = fc_d;
		if (fc_result != NULL)
			*fc_result = fc_closept;
	}

	fc_point.x = fc_box->high.x;
	fc_point.y = fc_box->low.y;
	fc_statlseg_construct(&fc_bseg, &fc_box->low, &fc_point);
	fc_d = fc_lseg_closept_lseg(&fc_closept, &fc_bseg, fc_lseg);
	if (float8_lt(fc_d, fc_dist))
	{
		fc_dist = fc_d;
		if (fc_result != NULL)
			*fc_result = fc_closept;
	}

	fc_statlseg_construct(&fc_bseg, &fc_box->high, &fc_point);
	fc_d = fc_lseg_closept_lseg(&fc_closept, &fc_bseg, fc_lseg);
	if (float8_lt(fc_d, fc_dist))
	{
		fc_dist = fc_d;
		if (fc_result != NULL)
			*fc_result = fc_closept;
	}

	return fc_dist;
}

Datum close_sb(PG_FUNCTION_ARGS)
{
	LSEG	   *fc_lseg = PG_GETARG_LSEG_P(0);
	BOX		   *fc_box = PG_GETARG_BOX_P(1);
	Point	   *fc_result;

	fc_result = (Point *) palloc(sizeof(Point));

	if (isnan(fc_box_closept_lseg(fc_result, fc_box, fc_lseg)))
		PG_RETURN_NULL();

	PG_RETURN_POINT_P(fc_result);
}


/*---------------------------------------------------------------------
 *		on_
 *				一个物体是否完全位于另一个物体内。
 *-------------------------------------------------------------------*/

/*
 *		该点是否满足方程？
 */
static bool fc_line_contain_point(LINE *fc_line, Point *fc_point)
{
	return FPzero(float8_pl(float8_pl(float8_mul(fc_line->A, fc_point->x),
									  float8_mul(fc_line->B, fc_point->y)),
							fc_line->C));
}

Datum on_pl(PG_FUNCTION_ARGS)
{
	Point	   *fc_pt = PG_GETARG_POINT_P(0);
	LINE	   *fc_line = PG_GETARG_LINE_P(1);

	PG_RETURN_BOOL(fc_line_contain_point(fc_line, fc_pt));
}


/*
 *		通过检测三角形不等式来确定共线性。
 * 这个算法似乎在处理 lsb 残差时表现良好 - tgl 1997-07-09
 */
static bool fc_lseg_contain_point(LSEG *fc_lseg, Point *fc_pt)
{
	return FPeq(fc_point_dt(fc_pt, &fc_lseg->p[0]) +
				fc_point_dt(fc_pt, &fc_lseg->p[1]),
				fc_point_dt(&fc_lseg->p[0], &fc_lseg->p[1]));
}

Datum on_ps(PG_FUNCTION_ARGS)
{
	Point	   *fc_pt = PG_GETARG_POINT_P(0);
	LSEG	   *fc_lseg = PG_GETARG_LSEG_P(1);

	PG_RETURN_BOOL(fc_lseg_contain_point(fc_lseg, fc_pt));
}


/*
 * 检查该点是否在框内或在其边界上
 */
static bool fc_box_contain_point(BOX *fc_box, Point *fc_point)
{
	return fc_box->high.x >= fc_point->x && fc_box->low.x <= fc_point->x &&
		fc_box->high.y >= fc_point->y && fc_box->low.y <= fc_point->y;
}

Datum on_pb(PG_FUNCTION_ARGS)
{
	Point	   *fc_pt = PG_GETARG_POINT_P(0);
	BOX		   *fc_box = PG_GETARG_BOX_P(1);

	PG_RETURN_BOOL(fc_box_contain_point(fc_box, fc_pt));
}

Datum box_contain_pt(PG_FUNCTION_ARGS)
{
	BOX		   *fc_box = PG_GETARG_BOX_P(0);
	Point	   *fc_pt = PG_GETARG_POINT_P(1);

	PG_RETURN_BOOL(fc_box_contain_point(fc_box, fc_pt));
}

/* on_ppath -
 *		一个点是否位于（在）折线内。
 *		如果是开放的，我们需要（唉）检查每个线段。
 * （使用与点相交线段相同的算法 - tgl 1997-07-09）
 *		如果是闭合的，我们使用旧的 O(n) 射线方法来判断点在多边形内。
 *				射线是水平的，从点向右延伸。
 *				每个与射线交叉的线段都算作一个交点；
 *				请注意，端点或边可能接触但不交叉。
 *				（我们可以在 lg(n) 中做点在多边形中，但需要预处理）
 */
Datum on_ppath(PG_FUNCTION_ARGS)
{
	Point	   *fc_pt = PG_GETARG_POINT_P(0);
	PATH	   *fc_path = PG_GETARG_PATH_P(1);
	int			fc_i,
				fc_n;
	float8		fc_a,
				fc_b;

	/*-- OPEN --*/
	if (!fc_path->closed)
	{
		fc_n = fc_path->npts - 1;
		fc_a = fc_point_dt(fc_pt, &fc_path->p[0]);
		for (fc_i = 0; fc_i < fc_n; fc_i++)
		{
			fc_b = fc_point_dt(fc_pt, &fc_path->p[fc_i + 1]);
			if (FPeq(float8_pl(fc_a, fc_b), fc_point_dt(&fc_path->p[fc_i], &fc_path->p[fc_i + 1])))
				PG_RETURN_BOOL(true);
			fc_a = fc_b;
		}
		PG_RETURN_BOOL(false);
	}

	/*-- 已闭合 --*/
	PG_RETURN_BOOL(fc_point_inside(fc_pt, fc_path->npts, fc_path->p) != 0);
}


/*
 * 检查线段是否在直线上或足够接近
 *
 * 如果它的两个点都在直线上或足够接近，则是的。
 */
Datum on_sl(PG_FUNCTION_ARGS)
{
	LSEG	   *fc_lseg = PG_GETARG_LSEG_P(0);
	LINE	   *fc_line = PG_GETARG_LINE_P(1);

	PG_RETURN_BOOL(fc_line_contain_point(fc_line, &fc_lseg->p[0]) &&
				   fc_line_contain_point(fc_line, &fc_lseg->p[1]));
}


/*
 * 检查线段是否在框内或在其边界上
 *
 * 如果它的两个点都在框内或在其边界上，则是的。
 */
static bool fc_box_contain_lseg(BOX *fc_box, LSEG *fc_lseg)
{
	return fc_box_contain_point(fc_box, &fc_lseg->p[0]) &&
		fc_box_contain_point(fc_box, &fc_lseg->p[1]);
}

Datum on_sb(PG_FUNCTION_ARGS)
{
	LSEG	   *fc_lseg = PG_GETARG_LSEG_P(0);
	BOX		   *fc_box = PG_GETARG_BOX_P(1);

	PG_RETURN_BOOL(fc_box_contain_lseg(fc_box, fc_lseg));
}

/*---------------------------------------------------------------------
 *		inter_
 *				一个物体是否与另一个物体相交。
 *-------------------------------------------------------------------*/

Datum inter_sl(PG_FUNCTION_ARGS)
{
	LSEG	   *fc_lseg = PG_GETARG_LSEG_P(0);
	LINE	   *fc_line = PG_GETARG_LINE_P(1);

	PG_RETURN_BOOL(fc_lseg_interpt_line(NULL, fc_lseg, fc_line));
}


/*
 * 线段和框是否相交？
 *
 * 完全在框内的线段算作相交。
 * 如果你只想要穿过框边界的线段，
 * 尝试先将框转换为路径。
 *
 * 当它们重叠且结果不为 NULL 时，
 * 该函数还将 *result 设置为线段到框中心的最近点。
 * 这是有些任意的，但可能是我们所能做的最好选择，
 * 因为它们交叉的点通常有两个。
 *
 * 通过先检查框的相交情况来优化非相交情况。
 * - thomas 1998-01-30
 */
static bool fc_box_interpt_lseg(Point *fc_result, BOX *fc_box, LSEG *fc_lseg)
{
	BOX			fc_lbox;
	LSEG		fc_bseg;
	Point		fc_point;

	fc_lbox.low.x = float8_min(fc_lseg->p[0].x, fc_lseg->p[1].x);
	fc_lbox.low.y = float8_min(fc_lseg->p[0].y, fc_lseg->p[1].y);
	fc_lbox.high.x = float8_max(fc_lseg->p[0].x, fc_lseg->p[1].x);
	fc_lbox.high.y = float8_max(fc_lseg->p[0].y, fc_lseg->p[1].y);

	/* 附近没有重叠？那么不会相交 */
	if (!fc_box_ov(&fc_lbox, fc_box))
		return false;

	if (fc_result != NULL)
	{
		fc_box_cn(&fc_point, fc_box);
		fc_lseg_closept_point(fc_result, fc_lseg, &fc_point);
	}

	/* 若线段的端点在框内？那么显然相交 */
	if (fc_box_contain_point(fc_box, &fc_lseg->p[0]) ||
		fc_box_contain_point(fc_box, &fc_lseg->p[1]))
		return true;

	/* 两段线相交的逐对检查 */
	fc_point.x = fc_box->low.x;
	fc_point.y = fc_box->high.y;
	fc_statlseg_construct(&fc_bseg, &fc_box->low, &fc_point);
	if (fc_lseg_interpt_lseg(NULL, &fc_bseg, fc_lseg))
		return true;

	fc_statlseg_construct(&fc_bseg, &fc_box->high, &fc_point);
	if (fc_lseg_interpt_lseg(NULL, &fc_bseg, fc_lseg))
		return true;

	fc_point.x = fc_box->high.x;
	fc_point.y = fc_box->low.y;
	fc_statlseg_construct(&fc_bseg, &fc_box->low, &fc_point);
	if (fc_lseg_interpt_lseg(NULL, &fc_bseg, fc_lseg))
		return true;

	fc_statlseg_construct(&fc_bseg, &fc_box->high, &fc_point);
	if (fc_lseg_interpt_lseg(NULL, &fc_bseg, fc_lseg))
		return true;

	/* 如果我们跳过了，则没有两个线段相交 */
	return false;
}

Datum inter_sb(PG_FUNCTION_ARGS)
{
	LSEG	   *fc_lseg = PG_GETARG_LSEG_P(0);
	BOX		   *fc_box = PG_GETARG_BOX_P(1);

	PG_RETURN_BOOL(fc_box_interpt_lseg(NULL, fc_box, fc_lseg));
}


/* inter_lb()
 * 线和框是否相交？
 */
Datum inter_lb(PG_FUNCTION_ARGS)
{
	LINE	   *fc_line = PG_GETARG_LINE_P(0);
	BOX		   *fc_box = PG_GETARG_BOX_P(1);
	LSEG		fc_bseg;
	Point		fc_p1,
				fc_p2;

	/* 两段线相交的逐对检查 */
	fc_p1.x = fc_box->low.x;
	fc_p1.y = fc_box->low.y;
	fc_p2.x = fc_box->low.x;
	fc_p2.y = fc_box->high.y;
	fc_statlseg_construct(&fc_bseg, &fc_p1, &fc_p2);
	if (fc_lseg_interpt_line(NULL, &fc_bseg, fc_line))
		PG_RETURN_BOOL(true);
	fc_p1.x = fc_box->high.x;
	fc_p1.y = fc_box->high.y;
	fc_statlseg_construct(&fc_bseg, &fc_p1, &fc_p2);
	if (fc_lseg_interpt_line(NULL, &fc_bseg, fc_line))
		PG_RETURN_BOOL(true);
	fc_p2.x = fc_box->high.x;
	fc_p2.y = fc_box->low.y;
	fc_statlseg_construct(&fc_bseg, &fc_p1, &fc_p2);
	if (fc_lseg_interpt_line(NULL, &fc_bseg, fc_line))
		PG_RETURN_BOOL(true);
	fc_p1.x = fc_box->low.x;
	fc_p1.y = fc_box->low.y;
	fc_statlseg_construct(&fc_bseg, &fc_p1, &fc_p2);
	if (fc_lseg_interpt_line(NULL, &fc_bseg, fc_line))
		PG_RETURN_BOOL(true);

	/* 如果我们没有通过，则没有相交 */
	PG_RETURN_BOOL(false);
}

/*------------------------------------------------------------------
 * 以下例程定义了一种数据类型和操作符类，对于
 * 多边形 .... 部分（多边形的包围框）建立在
 * BOX 数据类型之上。
 *
 * make_bound_box - 为输入多边形创建包围盒
 *------------------------------------------------------------------*/

/*---------------------------------------------------------------------
 * 为给定多边形制作最小的包围盒。
 *---------------------------------------------------------------------*/
static void fc_make_bound_box(POLYGON *fc_poly)
{
	int			fc_i;
	float8		fc_x1,
				fc_y1,
				fc_x2,
				fc_y2;

	Assert(fc_poly->npts > 0);

	fc_x1 = fc_x2 = fc_poly->p[0].x;
	fc_y2 = fc_y1 = fc_poly->p[0].y;
	for (fc_i = 1; fc_i < fc_poly->npts; fc_i++)
	{
		if (float8_lt(fc_poly->p[fc_i].x, fc_x1))
			fc_x1 = fc_poly->p[fc_i].x;
		if (float8_gt(fc_poly->p[fc_i].x, fc_x2))
			fc_x2 = fc_poly->p[fc_i].x;
		if (float8_lt(fc_poly->p[fc_i].y, fc_y1))
			fc_y1 = fc_poly->p[fc_i].y;
		if (float8_gt(fc_poly->p[fc_i].y, fc_y2))
			fc_y2 = fc_poly->p[fc_i].y;
	}

	fc_poly->boundbox.low.x = fc_x1;
	fc_poly->boundbox.high.x = fc_x2;
	fc_poly->boundbox.low.y = fc_y1;
	fc_poly->boundbox.high.y = fc_y2;
}


/*-----------------------------------------------------------------
 * poly_in - 从字符串规范读取多边形
 *
 *		外部格式：
 *				"((x0,y0),...,(xn,yn))"
 *				"x0,y0,...,xn,yn"
 *				还支持旧式"(x1,...,xn,y1,...yn)"
------------------------------------------------------------------*/
Datum poly_in(PG_FUNCTION_ARGS)
{
	char	   *fc_str = PG_GETARG_CSTRING(0);
	POLYGON    *fc_poly;
	int			fc_npts;
	int			fc_size;
	int			fc_base_size;
	bool		fc_isopen;

	if ((fc_npts = fc_pair_count(fc_str, ',')) <= 0)
#ifdef FDD //cppcheck
	{
#endif
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
				 errmsg("invalid input syntax for type %s: \"%s\"",
						"polygon", fc_str)));
#ifdef FDD //cppcheck
		PG_RETURN_VOID();
	}
#endif
	fc_base_size = sizeof(fc_poly->p[0]) * fc_npts;
	fc_size = offsetof(POLYGON, p) + fc_base_size;

	/* 检查整数溢出 */
	if (fc_base_size / fc_npts != sizeof(fc_poly->p[0]) || fc_size <= fc_base_size)
		ereport(ERROR,
				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
				 errmsg("too many points requested")));

	fc_poly = (POLYGON *) palloc0(fc_size);	/* 清除任何孔洞 */

	SET_VARSIZE(fc_poly, fc_size);
	fc_poly->npts = fc_npts;

	fc_path_decode(fc_str, false, fc_npts, &(fc_poly->p[0]), &fc_isopen, NULL, "polygon", fc_str);

	fc_make_bound_box(fc_poly);

	PG_RETURN_POLYGON_P(fc_poly);
}

/*---------------------------------------------------------------
 * poly_out - 将内部POLYGON表示转换为
 *			  字符串格式"((f8,f8),...,(f8,f8))"
---------------------------------------------------------------*/
Datum poly_out(PG_FUNCTION_ARGS)
{
	POLYGON    *fc_poly = PG_GETARG_POLYGON_P(0);

	PG_RETURN_CSTRING(fc_path_encode(PATH_CLOSED, fc_poly->npts, fc_poly->p));
}

/*
 *		poly_recv			- 将外部二进制格式转换为多边形
 *
 * 外部表示为int32点的数量和点。
 * 我们在读取时重新计算边界框，而不是信任它是
 * 有效的。 （检查它所需的时间是一样的，所以不如
 * 从外部表示中省略它。）
 */
Datum poly_recv(PG_FUNCTION_ARGS)
{
	StringInfo	fc_buf = (StringInfo) PG_GETARG_POINTER(0);
	POLYGON    *fc_poly;
	int32		fc_npts;
	int32		fc_i;
	int			fc_size;

	fc_npts = pq_getmsgint(fc_buf, sizeof(int32));
	if (fc_npts <= 0 || fc_npts >= (int32) ((INT_MAX - offsetof(POLYGON, p)) / sizeof(Point)))
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
				 errmsg("invalid number of points in external \"polygon\" value")));

	fc_size = offsetof(POLYGON, p) + sizeof(fc_poly->p[0]) * fc_npts;
	fc_poly = (POLYGON *) palloc0(fc_size);	/* 清除任何孔洞 */

	SET_VARSIZE(fc_poly, fc_size);
	fc_poly->npts = fc_npts;

	for (fc_i = 0; fc_i < fc_npts; fc_i++)
	{
		fc_poly->p[fc_i].x = pq_getmsgfloat8(fc_buf);
		fc_poly->p[fc_i].y = pq_getmsgfloat8(fc_buf);
	}

	fc_make_bound_box(fc_poly);

	PG_RETURN_POLYGON_P(fc_poly);
}

/*
 *		poly_send			- 将多边形转换为二进制格式
 */
Datum poly_send(PG_FUNCTION_ARGS)
{
	POLYGON    *fc_poly = PG_GETARG_POLYGON_P(0);
	StringInfoData fc_buf;
	int32		fc_i;

	pq_begintypsend(&fc_buf);
	pq_sendint32(&fc_buf, fc_poly->npts);
	for (fc_i = 0; fc_i < fc_poly->npts; fc_i++)
	{
		pq_sendfloat8(&fc_buf, fc_poly->p[fc_i].x);
		pq_sendfloat8(&fc_buf, fc_poly->p[fc_i].y);
	}
	PG_RETURN_BYTEA_P(pq_endtypsend(&fc_buf));
}


/*-------------------------------------------------------
 * 多边形 A 是否严格位于多边形 B 的左侧？即 A 的最右点是否位于 B 的最左点的左侧？
 *-------------------------------------------------------*/
Datum poly_left(PG_FUNCTION_ARGS)
{
	POLYGON    *fc_polya = PG_GETARG_POLYGON_P(0);
	POLYGON    *fc_polyb = PG_GETARG_POLYGON_P(1);
	bool		fc_result;

	fc_result = fc_polya->boundbox.high.x < fc_polyb->boundbox.low.x;

	/*
	 * 避免为浸入式输入泄漏内存 ... 需要用于 rtree 索引
	 */
	PG_FREE_IF_COPY(fc_polya, 0);
	PG_FREE_IF_COPY(fc_polyb, 1);

	PG_RETURN_BOOL(fc_result);
}

/*-------------------------------------------------------
 * 多边形 A 是否与多边形 B 重叠或位于其左侧？即 A 的最右点是否在 B 的最右点的左侧或重合？
 *-------------------------------------------------------*/
Datum poly_overleft(PG_FUNCTION_ARGS)
{
	POLYGON    *fc_polya = PG_GETARG_POLYGON_P(0);
	POLYGON    *fc_polyb = PG_GETARG_POLYGON_P(1);
	bool		fc_result;

	fc_result = fc_polya->boundbox.high.x <= fc_polyb->boundbox.high.x;

	/*
	 * 避免为浸入式输入泄漏内存 ... 需要用于 rtree 索引
	 */
	PG_FREE_IF_COPY(fc_polya, 0);
	PG_FREE_IF_COPY(fc_polyb, 1);

	PG_RETURN_BOOL(fc_result);
}

/*-------------------------------------------------------
 * 多边形 A 是否严格位于多边形 B 的右侧？即 A 的最左点是否在 B 的最右点的右侧？
 *-------------------------------------------------------*/
Datum poly_right(PG_FUNCTION_ARGS)
{
	POLYGON    *fc_polya = PG_GETARG_POLYGON_P(0);
	POLYGON    *fc_polyb = PG_GETARG_POLYGON_P(1);
	bool		fc_result;

	fc_result = fc_polya->boundbox.low.x > fc_polyb->boundbox.high.x;

	/*
	 * 避免为浸入式输入泄漏内存 ... 需要用于 rtree 索引
	 */
	PG_FREE_IF_COPY(fc_polya, 0);
	PG_FREE_IF_COPY(fc_polyb, 1);

	PG_RETURN_BOOL(fc_result);
}

/*-------------------------------------------------------
 * 多边形 A 是否与多边形 B 重叠或位于其右侧？即 A 的最左点是否在 B 的最左点的右侧或重合？
 *-------------------------------------------------------*/
Datum poly_overright(PG_FUNCTION_ARGS)
{
	POLYGON    *fc_polya = PG_GETARG_POLYGON_P(0);
	POLYGON    *fc_polyb = PG_GETARG_POLYGON_P(1);
	bool		fc_result;

	fc_result = fc_polya->boundbox.low.x >= fc_polyb->boundbox.low.x;

	/*
	 * 避免为浸入式输入泄漏内存 ... 需要用于 rtree 索引
	 */
	PG_FREE_IF_COPY(fc_polya, 0);
	PG_FREE_IF_COPY(fc_polyb, 1);

	PG_RETURN_BOOL(fc_result);
}

/*-------------------------------------------------------
 * 多边形 A 是否严格位于多边形 B 的下方？即 A 的最上点是否在 B 的最下点的下方？
 *-------------------------------------------------------*/
Datum poly_below(PG_FUNCTION_ARGS)
{
	POLYGON    *fc_polya = PG_GETARG_POLYGON_P(0);
	POLYGON    *fc_polyb = PG_GETARG_POLYGON_P(1);
	bool		fc_result;

	fc_result = fc_polya->boundbox.high.y < fc_polyb->boundbox.low.y;

	/*
	 * 避免为浸入式输入泄漏内存 ... 需要用于 rtree 索引
	 */
	PG_FREE_IF_COPY(fc_polya, 0);
	PG_FREE_IF_COPY(fc_polyb, 1);

	PG_RETURN_BOOL(fc_result);
}

/*-------------------------------------------------------
 * 多边形 A 是否与多边形 B 重叠或位于其下方？即 A 的最上点是否在 B 的最上点的下方或重合？
 *-------------------------------------------------------*/
Datum poly_overbelow(PG_FUNCTION_ARGS)
{
	POLYGON    *fc_polya = PG_GETARG_POLYGON_P(0);
	POLYGON    *fc_polyb = PG_GETARG_POLYGON_P(1);
	bool		fc_result;

	fc_result = fc_polya->boundbox.high.y <= fc_polyb->boundbox.high.y;

	/*
	 * 避免为浸入式输入泄漏内存 ... 需要用于 rtree 索引
	 */
	PG_FREE_IF_COPY(fc_polya, 0);
	PG_FREE_IF_COPY(fc_polyb, 1);

	PG_RETURN_BOOL(fc_result);
}

/*-------------------------------------------------------
 * 多边形 A 是否严格位于多边形 B 的上方？即 A 的最下点是否在 B 的最上点的上方？
 *-------------------------------------------------------*/
Datum poly_above(PG_FUNCTION_ARGS)
{
	POLYGON    *fc_polya = PG_GETARG_POLYGON_P(0);
	POLYGON    *fc_polyb = PG_GETARG_POLYGON_P(1);
	bool		fc_result;

	fc_result = fc_polya->boundbox.low.y > fc_polyb->boundbox.high.y;

	/*
	 * 避免为浸入式输入泄漏内存 ... 需要用于 rtree 索引
	 */
	PG_FREE_IF_COPY(fc_polya, 0);
	PG_FREE_IF_COPY(fc_polyb, 1);

	PG_RETURN_BOOL(fc_result);
}

/*-------------------------------------------------------
 * 多边形 A 是否与多边形 B 重叠或位于其上方？即 A 的最下点是否在 B 的最下点的上方或重合？
 *-------------------------------------------------------*/
Datum poly_overabove(PG_FUNCTION_ARGS)
{
	POLYGON    *fc_polya = PG_GETARG_POLYGON_P(0);
	POLYGON    *fc_polyb = PG_GETARG_POLYGON_P(1);
	bool		fc_result;

	fc_result = fc_polya->boundbox.low.y >= fc_polyb->boundbox.low.y;

	/*
	 * 避免为浸入式输入泄漏内存 ... 需要用于 rtree 索引
	 */
	PG_FREE_IF_COPY(fc_polya, 0);
	PG_FREE_IF_COPY(fc_polyb, 1);

	PG_RETURN_BOOL(fc_result);
}


/*-------------------------------------------------------
 * 多边形 A 是否与多边形 B 相同？即所有点是否都相同？
 * 检查双方所有点的匹配，包括正向和反向方向，
 * 因为多边形是无方向和闭合形状。
 *-------------------------------------------------------*/
Datum poly_same(PG_FUNCTION_ARGS)
{
	POLYGON    *fc_polya = PG_GETARG_POLYGON_P(0);
	POLYGON    *fc_polyb = PG_GETARG_POLYGON_P(1);
	bool		fc_result;

	if (fc_polya->npts != fc_polyb->npts)
		fc_result = false;
	else
		fc_result = fc_plist_same(fc_polya->npts, fc_polya->p, fc_polyb->p);

	/*
	 * 避免为浸入式输入泄漏内存 ... 需要用于 rtree 索引
	 */
	PG_FREE_IF_COPY(fc_polya, 0);
	PG_FREE_IF_COPY(fc_polyb, 1);

	PG_RETURN_BOOL(fc_result);
}

/*-----------------------------------------------------------------
 * 确定多边形 A 是否与多边形 B 重叠
 *-----------------------------------------------------------------*/
static bool fc_poly_overlap_internal(POLYGON *fc_polya, POLYGON *fc_polyb)
{
	bool		fc_result;

	Assert(fc_polya->npts > 0 && fc_polyb->npts > 0);

	/* 通过边界框进行快速检查 */
	fc_result = fc_box_ov(&fc_polya->boundbox, &fc_polyb->boundbox);

	/*
	 * 鲁棒算法 - 尝试查找交叉边，如果有，则多边形重叠，否则检查一个多边形是否在另一个内部
	 * 通过测试它们的单点。
	 */
	if (fc_result)
	{
		int			fc_ia,
					fc_ib;
		LSEG		fc_sa,
					fc_sb;

		/* 初始化多边形 A 边的第一个点为最后一个点 */
		fc_sa.p[0] = fc_polya->p[fc_polya->npts - 1];
		fc_result = false;

		for (fc_ia = 0; fc_ia < fc_polya->npts && !fc_result; fc_ia++)
		{
			/* 多边形 A 边的第二个点为当前点 */
			fc_sa.p[1] = fc_polya->p[fc_ia];

			/* 初始化多边形 B 边的第一个点为最后一个点 */
			fc_sb.p[0] = fc_polyb->p[fc_polyb->npts - 1];

			for (fc_ib = 0; fc_ib < fc_polyb->npts && !fc_result; fc_ib++)
			{
				fc_sb.p[1] = fc_polyb->p[fc_ib];
				fc_result = fc_lseg_interpt_lseg(NULL, &fc_sa, &fc_sb);
				fc_sb.p[0] = fc_sb.p[1];
			}

			/*
			 * 将当前端点移动到下一个边的第一个点
			 */
			fc_sa.p[0] = fc_sa.p[1];
		}

		if (!fc_result)
		{
			fc_result = (fc_point_inside(fc_polya->p, fc_polyb->npts, fc_polyb->p) ||
					  fc_point_inside(fc_polyb->p, fc_polya->npts, fc_polya->p));
		}
	}

	return fc_result;
}

Datum poly_overlap(PG_FUNCTION_ARGS)
{
	POLYGON    *fc_polya = PG_GETARG_POLYGON_P(0);
	POLYGON    *fc_polyb = PG_GETARG_POLYGON_P(1);
	bool		fc_result;

	fc_result = fc_poly_overlap_internal(fc_polya, fc_polyb);

	/*
	 * 避免为浸入式输入泄漏内存 ... 需要用于 rtree 索引
	 */
	PG_FREE_IF_COPY(fc_polya, 0);
	PG_FREE_IF_COPY(fc_polyb, 1);

	PG_RETURN_BOOL(fc_result);
}

/*
 * 测试特殊类型的线段是否在多边形内/外。
 * 特殊类型意味着：
 *	- 点 a 应该在线段 s 上
 *	- 线段 (a,b) 不应被 s 包含
 * 如果返回真，则：
 *	- 线段 (a,b) 与 s 是共线的，并且 (a,b) 在多边形内
 *	- 线段 (a,b) 不与 s 共线。注意，这并不
 *	  意味着线段在多边形内！
 */

static bool fc_touched_lseg_inside_poly(Point *fc_a, Point *fc_b, LSEG *fc_s, POLYGON *fc_poly, int fc_start)
{
	/* 点 a 在 s 上，b 不在 */
	LSEG		fc_t;

	fc_t.p[0] = *fc_a;
	fc_t.p[1] = *fc_b;

	if (fc_point_eq_point(fc_a, fc_s->p))
	{
		if (fc_lseg_contain_point(&fc_t, fc_s->p + 1))
			return fc_lseg_inside_poly(fc_b, fc_s->p + 1, fc_poly, fc_start);
	}
	else if (fc_point_eq_point(fc_a, fc_s->p + 1))
	{
		if (fc_lseg_contain_point(&fc_t, fc_s->p))
			return fc_lseg_inside_poly(fc_b, fc_s->p, fc_poly, fc_start);
	}
	else if (fc_lseg_contain_point(&fc_t, fc_s->p))
	{
		return fc_lseg_inside_poly(fc_b, fc_s->p, fc_poly, fc_start);
	}
	else if (fc_lseg_contain_point(&fc_t, fc_s->p + 1))
	{
		return fc_lseg_inside_poly(fc_b, fc_s->p + 1, fc_poly, fc_start);
	}

	return true;				/* 可能不是真的，但会稍后检查 */
}

/*
 * 如果线段 (a,b) 在多边形内，则返回真，选项
 * start 用于优化 - 函数检查
 * 从 start 开始的多边形边
 */
static bool fc_lseg_inside_poly(Point *fc_a, Point *fc_b, POLYGON *fc_poly, int fc_start)
{
	LSEG		fc_s,
				fc_t;
	int			fc_i;
	bool		fc_res = true,
				fc_intersection = false;

	/* 由于这个函数递归，可能会导致栈溢出 */
	check_stack_depth();

	fc_t.p[0] = *fc_a;
	fc_t.p[1] = *fc_b;
	fc_s.p[0] = fc_poly->p[(fc_start == 0) ? (fc_poly->npts - 1) : (fc_start - 1)];

	for (fc_i = fc_start; fc_i < fc_poly->npts && fc_res; fc_i++)
	{
		Point		fc_interpt;

		CHECK_FOR_INTERRUPTS();

		fc_s.p[1] = fc_poly->p[fc_i];

		if (fc_lseg_contain_point(&fc_s, fc_t.p))
		{
			if (fc_lseg_contain_point(&fc_s, fc_t.p + 1))
				return true;	/* t 被 s 包含 */

			/* Y-cross */
			fc_res = fc_touched_lseg_inside_poly(fc_t.p, fc_t.p + 1, &fc_s, fc_poly, fc_i + 1);
		}
		else if (fc_lseg_contain_point(&fc_s, fc_t.p + 1))
		{
			/* Y-cross */
			fc_res = fc_touched_lseg_inside_poly(fc_t.p + 1, fc_t.p, &fc_s, fc_poly, fc_i + 1);
		}
		else if (fc_lseg_interpt_lseg(&fc_interpt, &fc_t, &fc_s))
		{
			/*
			 * 线段是交叉的，继续检查每个子线段
			 */

			fc_intersection = true;
			fc_res = fc_lseg_inside_poly(fc_t.p, &fc_interpt, fc_poly, fc_i + 1);
			if (fc_res)
				fc_res = fc_lseg_inside_poly(fc_t.p + 1, &fc_interpt, fc_poly, fc_i + 1);
		}

		fc_s.p[0] = fc_s.p[1];
	}

	if (fc_res && !fc_intersection)
	{
		Point		fc_p;

		/*
		 * 如果没有找到交叉，则检查被测试线段的中心点。
		 * 在另一方面我们已经检查了所有子线段
		 */
		fc_p.x = float8_div(float8_pl(fc_t.p[0].x, fc_t.p[1].x), 2.0);
		fc_p.y = float8_div(float8_pl(fc_t.p[0].y, fc_t.p[1].y), 2.0);

		fc_res = fc_point_inside(&fc_p, fc_poly->npts, fc_poly->p);
	}

	return fc_res;
}

/*
 * 检查第一个多边形是否包含第二个多边形
 */
static bool fc_poly_contain_poly(POLYGON *fc_contains_poly, POLYGON *fc_contained_poly)
{
	int			fc_i;
	LSEG		fc_s;

	Assert(fc_contains_poly->npts > 0 && fc_contained_poly->npts > 0);

	/*
	 * 快速检查被包含的边界框是否被包含的边界框所包含。
	 */
	if (!fc_box_contain_box(&fc_contains_poly->boundbox, &fc_contained_poly->boundbox))
		return false;

	fc_s.p[0] = fc_contained_poly->p[fc_contained_poly->npts - 1];

	for (fc_i = 0; fc_i < fc_contained_poly->npts; fc_i++)
	{
		fc_s.p[1] = fc_contained_poly->p[fc_i];
		if (!fc_lseg_inside_poly(fc_s.p, fc_s.p + 1, fc_contains_poly, 0))
			return false;
		fc_s.p[0] = fc_s.p[1];
	}

	return true;
}

Datum poly_contain(PG_FUNCTION_ARGS)
{
	POLYGON    *fc_polya = PG_GETARG_POLYGON_P(0);
	POLYGON    *fc_polyb = PG_GETARG_POLYGON_P(1);
	bool		fc_result;

	fc_result = fc_poly_contain_poly(fc_polya, fc_polyb);

	/*
	 * 避免为浸入式输入泄漏内存 ... 需要用于 rtree 索引
	 */
	PG_FREE_IF_COPY(fc_polya, 0);
	PG_FREE_IF_COPY(fc_polyb, 1);

	PG_RETURN_BOOL(fc_result);
}


/*-----------------------------------------------------------------
 * 确定多边形 A 是否被多边形 B 包含
 *-----------------------------------------------------------------*/
Datum poly_contained(PG_FUNCTION_ARGS)
{
	POLYGON    *fc_polya = PG_GETARG_POLYGON_P(0);
	POLYGON    *fc_polyb = PG_GETARG_POLYGON_P(1);
	bool		fc_result;

	/* 只需交换参数并将其传递给 poly_contain */
	fc_result = fc_poly_contain_poly(fc_polyb, fc_polya);

	/*
	 * 避免为浸入式输入泄漏内存 ... 需要用于 rtree 索引
	 */
	PG_FREE_IF_COPY(fc_polya, 0);
	PG_FREE_IF_COPY(fc_polyb, 1);

	PG_RETURN_BOOL(fc_result);
}


Datum poly_contain_pt(PG_FUNCTION_ARGS)
{
	POLYGON    *fc_poly = PG_GETARG_POLYGON_P(0);
	Point	   *fc_p = PG_GETARG_POINT_P(1);

	PG_RETURN_BOOL(fc_point_inside(fc_p, fc_poly->npts, fc_poly->p) != 0);
}

Datum pt_contained_poly(PG_FUNCTION_ARGS)
{
	Point	   *fc_p = PG_GETARG_POINT_P(0);
	POLYGON    *fc_poly = PG_GETARG_POLYGON_P(1);

	PG_RETURN_BOOL(fc_point_inside(fc_p, fc_poly->npts, fc_poly->p) != 0);
}


Datum poly_distance(PG_FUNCTION_ARGS)
{
	POLYGON    *fc_polya = PG_GETARG_POLYGON_P(0);
	POLYGON    *fc_polyb = PG_GETARG_POLYGON_P(1);
	float8		fc_min = 0.0;		/* 初始化以保持编译器安静 */
	bool		fc_have_min = false;
	float8		fc_tmp;
	int			fc_i,
				fc_j;
	LSEG		fc_seg1,
				fc_seg2;

	/*
	 * 如果多边形重叠，则距离为零。
	 * 我们必须检查这一点，因为路径距离在一个多边形完全包含在另一个多边形内时将不会给出正确的答案。
	 */
	if (fc_poly_overlap_internal(fc_polya, fc_polyb))
		PG_RETURN_FLOAT8(0.0);

	/*
	 * 当它们不重叠时，距离计算与闭合路径的计算是相同的
	 * （即，我们不需要关心多边形包含其包含区域的事实）。请参见 path_distance()。
	 */
	for (fc_i = 0; fc_i < fc_polya->npts; fc_i++)
	{
		int			fc_iprev;

		if (fc_i > 0)
			fc_iprev = fc_i - 1;
		else
			fc_iprev = fc_polya->npts - 1;

		for (fc_j = 0; fc_j < fc_polyb->npts; fc_j++)
		{
			int			fc_jprev;

			if (fc_j > 0)
				fc_jprev = fc_j - 1;
			else
				fc_jprev = fc_polyb->npts - 1;

			fc_statlseg_construct(&fc_seg1, &fc_polya->p[fc_iprev], &fc_polya->p[fc_i]);
			fc_statlseg_construct(&fc_seg2, &fc_polyb->p[fc_jprev], &fc_polyb->p[fc_j]);

			fc_tmp = fc_lseg_closept_lseg(NULL, &fc_seg1, &fc_seg2);
			if (!fc_have_min || float8_lt(fc_tmp, fc_min))
			{
				fc_min = fc_tmp;
				fc_have_min = true;
			}
		}
	}

	if (!fc_have_min)
		PG_RETURN_NULL();

	PG_RETURN_FLOAT8(fc_min);
}


/***********************************************************************
 **
 **		二维点的例程。
 **
 ***********************************************************************/

Datum construct_point(PG_FUNCTION_ARGS)
{
	float8		fc_x = PG_GETARG_FLOAT8(0);
	float8		fc_y = PG_GETARG_FLOAT8(1);
	Point	   *fc_result;

	fc_result = (Point *) palloc(sizeof(Point));

	fc_point_construct(fc_result, fc_x, fc_y);

	PG_RETURN_POINT_P(fc_result);
}


static inline void fc_point_add_point(Point *fc_result, Point *fc_pt1, Point *fc_pt2)
{
	fc_point_construct(fc_result,
					float8_pl(fc_pt1->x, fc_pt2->x),
					float8_pl(fc_pt1->y, fc_pt2->y));
}

Datum point_add(PG_FUNCTION_ARGS)
{
	Point	   *fc_p1 = PG_GETARG_POINT_P(0);
	Point	   *fc_p2 = PG_GETARG_POINT_P(1);
	Point	   *fc_result;

	fc_result = (Point *) palloc(sizeof(Point));

	fc_point_add_point(fc_result, fc_p1, fc_p2);

	PG_RETURN_POINT_P(fc_result);
}


static inline void fc_point_sub_point(Point *fc_result, Point *fc_pt1, Point *fc_pt2)
{
	fc_point_construct(fc_result,
					float8_mi(fc_pt1->x, fc_pt2->x),
					float8_mi(fc_pt1->y, fc_pt2->y));
}

Datum point_sub(PG_FUNCTION_ARGS)
{
	Point	   *fc_p1 = PG_GETARG_POINT_P(0);
	Point	   *fc_p2 = PG_GETARG_POINT_P(1);
	Point	   *fc_result;

	fc_result = (Point *) palloc(sizeof(Point));

	fc_point_sub_point(fc_result, fc_p1, fc_p2);

	PG_RETURN_POINT_P(fc_result);
}


static inline void fc_point_mul_point(Point *fc_result, Point *fc_pt1, Point *fc_pt2)
{
	fc_point_construct(fc_result,
					float8_mi(float8_mul(fc_pt1->x, fc_pt2->x),
							  float8_mul(fc_pt1->y, fc_pt2->y)),
					float8_pl(float8_mul(fc_pt1->x, fc_pt2->y),
							  float8_mul(fc_pt1->y, fc_pt2->x)));
}

Datum point_mul(PG_FUNCTION_ARGS)
{
	Point	   *fc_p1 = PG_GETARG_POINT_P(0);
	Point	   *fc_p2 = PG_GETARG_POINT_P(1);
	Point	   *fc_result;

	fc_result = (Point *) palloc(sizeof(Point));

	fc_point_mul_point(fc_result, fc_p1, fc_p2);

	PG_RETURN_POINT_P(fc_result);
}


static inline void fc_point_div_point(Point *fc_result, Point *fc_pt1, Point *fc_pt2)
{
	float8		fc_div;

	fc_div = float8_pl(float8_mul(fc_pt2->x, fc_pt2->x), float8_mul(fc_pt2->y, fc_pt2->y));

	fc_point_construct(fc_result,
					float8_div(float8_pl(float8_mul(fc_pt1->x, fc_pt2->x),
										 float8_mul(fc_pt1->y, fc_pt2->y)), fc_div),
					float8_div(float8_mi(float8_mul(fc_pt1->y, fc_pt2->x),
										 float8_mul(fc_pt1->x, fc_pt2->y)), fc_div));
}

Datum point_div(PG_FUNCTION_ARGS)
{
	Point	   *fc_p1 = PG_GETARG_POINT_P(0);
	Point	   *fc_p2 = PG_GETARG_POINT_P(1);
	Point	   *fc_result;

	fc_result = (Point *) palloc(sizeof(Point));

	fc_point_div_point(fc_result, fc_p1, fc_p2);

	PG_RETURN_POINT_P(fc_result);
}


/***********************************************************************
 **
 **		2D盒子的例程。
 **
 ***********************************************************************/

Datum points_box(PG_FUNCTION_ARGS)
{
	Point	   *fc_p1 = PG_GETARG_POINT_P(0);
	Point	   *fc_p2 = PG_GETARG_POINT_P(1);
	BOX		   *fc_result;

	fc_result = (BOX *) palloc(sizeof(BOX));

	fc_box_construct(fc_result, fc_p1, fc_p2);

	PG_RETURN_BOX_P(fc_result);
}

Datum box_add(PG_FUNCTION_ARGS)
{
	BOX		   *fc_box = PG_GETARG_BOX_P(0);
	Point	   *fc_p = PG_GETARG_POINT_P(1);
	BOX		   *fc_result;

	fc_result = (BOX *) palloc(sizeof(BOX));

	fc_point_add_point(&fc_result->high, &fc_box->high, fc_p);
	fc_point_add_point(&fc_result->low, &fc_box->low, fc_p);

	PG_RETURN_BOX_P(fc_result);
}

Datum box_sub(PG_FUNCTION_ARGS)
{
	BOX		   *fc_box = PG_GETARG_BOX_P(0);
	Point	   *fc_p = PG_GETARG_POINT_P(1);
	BOX		   *fc_result;

	fc_result = (BOX *) palloc(sizeof(BOX));

	fc_point_sub_point(&fc_result->high, &fc_box->high, fc_p);
	fc_point_sub_point(&fc_result->low, &fc_box->low, fc_p);

	PG_RETURN_BOX_P(fc_result);
}

Datum box_mul(PG_FUNCTION_ARGS)
{
	BOX		   *fc_box = PG_GETARG_BOX_P(0);
	Point	   *fc_p = PG_GETARG_POINT_P(1);
	BOX		   *fc_result;
	Point		fc_high,
				fc_low;

	fc_result = (BOX *) palloc(sizeof(BOX));

	fc_point_mul_point(&fc_high, &fc_box->high, fc_p);
	fc_point_mul_point(&fc_low, &fc_box->low, fc_p);

	fc_box_construct(fc_result, &fc_high, &fc_low);

	PG_RETURN_BOX_P(fc_result);
}

Datum box_div(PG_FUNCTION_ARGS)
{
	BOX		   *fc_box = PG_GETARG_BOX_P(0);
	Point	   *fc_p = PG_GETARG_POINT_P(1);
	BOX		   *fc_result;
	Point		fc_high,
				fc_low;

	fc_result = (BOX *) palloc(sizeof(BOX));

	fc_point_div_point(&fc_high, &fc_box->high, fc_p);
	fc_point_div_point(&fc_low, &fc_box->low, fc_p);

	fc_box_construct(fc_result, &fc_high, &fc_low);

	PG_RETURN_BOX_P(fc_result);
}

/*
 * 将点转换为空盒
 */
Datum point_box(PG_FUNCTION_ARGS)
{
	Point	   *fc_pt = PG_GETARG_POINT_P(0);
	BOX		   *fc_box;

	fc_box = (BOX *) palloc(sizeof(BOX));

	fc_box->high.x = fc_pt->x;
	fc_box->low.x = fc_pt->x;
	fc_box->high.y = fc_pt->y;
	fc_box->low.y = fc_pt->y;

	PG_RETURN_BOX_P(fc_box);
}

/*
 * 包含两个给定盒子的最小边界盒
 */
Datum boxes_bound_box(PG_FUNCTION_ARGS)
{
	BOX		   *fc_box1 = PG_GETARG_BOX_P(0),
			   *fc_box2 = PG_GETARG_BOX_P(1),
			   *fc_container;

	fc_container = (BOX *) palloc(sizeof(BOX));

	fc_container->high.x = float8_max(fc_box1->high.x, fc_box2->high.x);
	fc_container->low.x = float8_min(fc_box1->low.x, fc_box2->low.x);
	fc_container->high.y = float8_max(fc_box1->high.y, fc_box2->high.y);
	fc_container->low.y = float8_min(fc_box1->low.y, fc_box2->low.y);

	PG_RETURN_BOX_P(fc_container);
}


/***********************************************************************
 **
 **		2D路径的例程。
 **
 ***********************************************************************/

/* path_add()
 * 连接两个路径（仅在它们都是开放的情况下）。
 */
Datum path_add(PG_FUNCTION_ARGS)
{
	PATH	   *fc_p1 = PG_GETARG_PATH_P(0);
	PATH	   *fc_p2 = PG_GETARG_PATH_P(1);
	PATH	   *fc_result;
	int			fc_size,
				fc_base_size;
	int			fc_i;

	if (fc_p1->closed || fc_p2->closed)
		PG_RETURN_NULL();

	fc_base_size = sizeof(fc_p1->p[0]) * (fc_p1->npts + fc_p2->npts);
	fc_size = offsetof(PATH, p) + fc_base_size;

	/* 检查整数溢出 */
	if (fc_base_size / sizeof(fc_p1->p[0]) != (fc_p1->npts + fc_p2->npts) ||
		fc_size <= fc_base_size)
		ereport(ERROR,
				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
				 errmsg("too many points requested")));

	fc_result = (PATH *) palloc(fc_size);

	SET_VARSIZE(fc_result, fc_size);
	fc_result->npts = (fc_p1->npts + fc_p2->npts);
	fc_result->closed = fc_p1->closed;
	/* 防止未使用填充字节的不稳定性 */
	fc_result->dummy = 0;

	for (fc_i = 0; fc_i < fc_p1->npts; fc_i++)
	{
		fc_result->p[fc_i].x = fc_p1->p[fc_i].x;
		fc_result->p[fc_i].y = fc_p1->p[fc_i].y;
	}
	for (fc_i = 0; fc_i < fc_p2->npts; fc_i++)
	{
		fc_result->p[fc_i + fc_p1->npts].x = fc_p2->p[fc_i].x;
		fc_result->p[fc_i + fc_p1->npts].y = fc_p2->p[fc_i].y;
	}

	PG_RETURN_PATH_P(fc_result);
}

/* path_add_pt()
 * 平移操作符。
 */
Datum path_add_pt(PG_FUNCTION_ARGS)
{
	PATH	   *fc_path = PG_GETARG_PATH_P_COPY(0);
	Point	   *fc_point = PG_GETARG_POINT_P(1);
	int			fc_i;

	for (fc_i = 0; fc_i < fc_path->npts; fc_i++)
		fc_point_add_point(&fc_path->p[fc_i], &fc_path->p[fc_i], fc_point);

	PG_RETURN_PATH_P(fc_path);
}

Datum path_sub_pt(PG_FUNCTION_ARGS)
{
	PATH	   *fc_path = PG_GETARG_PATH_P_COPY(0);
	Point	   *fc_point = PG_GETARG_POINT_P(1);
	int			fc_i;

	for (fc_i = 0; fc_i < fc_path->npts; fc_i++)
		fc_point_sub_point(&fc_path->p[fc_i], &fc_path->p[fc_i], fc_point);

	PG_RETURN_PATH_P(fc_path);
}

/* path_mul_pt()
 * 旋转和缩放操作符。
 */
Datum path_mul_pt(PG_FUNCTION_ARGS)
{
	PATH	   *fc_path = PG_GETARG_PATH_P_COPY(0);
	Point	   *fc_point = PG_GETARG_POINT_P(1);
	int			fc_i;

	for (fc_i = 0; fc_i < fc_path->npts; fc_i++)
		fc_point_mul_point(&fc_path->p[fc_i], &fc_path->p[fc_i], fc_point);

	PG_RETURN_PATH_P(fc_path);
}

Datum path_div_pt(PG_FUNCTION_ARGS)
{
	PATH	   *fc_path = PG_GETARG_PATH_P_COPY(0);
	Point	   *fc_point = PG_GETARG_POINT_P(1);
	int			fc_i;

	for (fc_i = 0; fc_i < fc_path->npts; fc_i++)
		fc_point_div_point(&fc_path->p[fc_i], &fc_path->p[fc_i], fc_point);

	PG_RETURN_PATH_P(fc_path);
}


Datum path_poly(PG_FUNCTION_ARGS)
{
	PATH	   *fc_path = PG_GETARG_PATH_P(0);
	POLYGON    *fc_poly;
	int			fc_size;
	int			fc_i;

	/* 这并不是非常一致---其他类似情况返回 NULL... */
	if (!fc_path->closed)
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
				 errmsg("open path cannot be converted to polygon")));

	/*
	 * 从不溢出：旧大小适合 MaxAllocSize，新大小只大了一个小常量。
	 */
	fc_size = offsetof(POLYGON, p) + sizeof(fc_poly->p[0]) * fc_path->npts;
	fc_poly = (POLYGON *) palloc(fc_size);

	SET_VARSIZE(fc_poly, fc_size);
	fc_poly->npts = fc_path->npts;

	for (fc_i = 0; fc_i < fc_path->npts; fc_i++)
	{
		fc_poly->p[fc_i].x = fc_path->p[fc_i].x;
		fc_poly->p[fc_i].y = fc_path->p[fc_i].y;
	}

	fc_make_bound_box(fc_poly);

	PG_RETURN_POLYGON_P(fc_poly);
}


/***********************************************************************
 **
 **		2D多边形的例程。
 **
 ***********************************************************************/

Datum poly_npoints(PG_FUNCTION_ARGS)
{
	POLYGON    *fc_poly = PG_GETARG_POLYGON_P(0);

	PG_RETURN_INT32(fc_poly->npts);
}


Datum poly_center(PG_FUNCTION_ARGS)
{
	POLYGON    *fc_poly = PG_GETARG_POLYGON_P(0);
	Point	   *fc_result;
	CIRCLE		fc_circle;

	fc_result = (Point *) palloc(sizeof(Point));

	fc_poly_to_circle(&fc_circle, fc_poly);
	*fc_result = fc_circle.center;

	PG_RETURN_POINT_P(fc_result);
}


Datum poly_box(PG_FUNCTION_ARGS)
{
	POLYGON    *fc_poly = PG_GETARG_POLYGON_P(0);
	BOX		   *fc_box;

	fc_box = (BOX *) palloc(sizeof(BOX));
	*fc_box = fc_poly->boundbox;

	PG_RETURN_BOX_P(fc_box);
}


/* box_poly()
 * 将盒子转换为多边形。
 */
Datum box_poly(PG_FUNCTION_ARGS)
{
	BOX		   *fc_box = PG_GETARG_BOX_P(0);
	POLYGON    *fc_poly;
	int			fc_size;

	/* 将盒子的四个角映射到一个多边形 */
	fc_size = offsetof(POLYGON, p) + sizeof(fc_poly->p[0]) * 4;
	fc_poly = (POLYGON *) palloc(fc_size);

	SET_VARSIZE(fc_poly, fc_size);
	fc_poly->npts = 4;

	fc_poly->p[0].x = fc_box->low.x;
	fc_poly->p[0].y = fc_box->low.y;
	fc_poly->p[1].x = fc_box->low.x;
	fc_poly->p[1].y = fc_box->high.y;
	fc_poly->p[2].x = fc_box->high.x;
	fc_poly->p[2].y = fc_box->high.y;
	fc_poly->p[3].x = fc_box->high.x;
	fc_poly->p[3].y = fc_box->low.y;

	fc_box_construct(&fc_poly->boundbox, &fc_box->high, &fc_box->low);

	PG_RETURN_POLYGON_P(fc_poly);
}


Datum poly_path(PG_FUNCTION_ARGS)
{
	POLYGON    *fc_poly = PG_GETARG_POLYGON_P(0);
	PATH	   *fc_path;
	int			fc_size;
	int			fc_i;

	/*
	 * 从不溢出：旧大小适合 MaxAllocSize，新大小因一个小常量而变小。
	 */
	fc_size = offsetof(PATH, p) + sizeof(fc_path->p[0]) * fc_poly->npts;
	fc_path = (PATH *) palloc(fc_size);

	SET_VARSIZE(fc_path, fc_size);
	fc_path->npts = fc_poly->npts;
	fc_path->closed = true;
	/* 防止未使用填充字节的不稳定性 */
	fc_path->dummy = 0;

	for (fc_i = 0; fc_i < fc_poly->npts; fc_i++)
	{
		fc_path->p[fc_i].x = fc_poly->p[fc_i].x;
		fc_path->p[fc_i].y = fc_poly->p[fc_i].y;
	}

	PG_RETURN_PATH_P(fc_path);
}


/***********************************************************************
 **
 **		圆的例程。
 **
 ***********************************************************************/

/*----------------------------------------------------------
 * 格式化和转换例程。
 *---------------------------------------------------------*/

/*		circle_in		-		将字符串转换为内部形式。
 *
 *		外部格式：（圆心和半径）
 *				"<(f8,f8),f8>"
 *				也支持快速输入风格 "f8,f8,f8"
 */
Datum circle_in(PG_FUNCTION_ARGS)
{
	char	   *fc_str = PG_GETARG_CSTRING(0);
	CIRCLE	   *fc_circle = (CIRCLE *) palloc(sizeof(CIRCLE));
	char	   *fc_s,
			   *fc_cp;
	int			fc_depth = 0;

	fc_s = fc_str;
	while (isspace((unsigned char) *fc_s))
		fc_s++;
	if (*fc_s == LDELIM_C)
		fc_depth++, fc_s++;
	else if (*fc_s == LDELIM)
	{
		/* 如果有两个左括号，消耗第一个 */
		fc_cp = (fc_s + 1);
		while (isspace((unsigned char) *fc_cp))
			fc_cp++;
		if (*fc_cp == LDELIM)
			fc_depth++, fc_s = fc_cp;
	}

	/* pair_decode 将消耗成对的括号（如果有的话） */
	fc_pair_decode(fc_s, &fc_circle->center.x, &fc_circle->center.y, &fc_s, "circle", fc_str);

	if (*fc_s == DELIM)
		fc_s++;

	fc_circle->radius = fc_single_decode(fc_s, &fc_s, "circle", fc_str);
	/* 我们必须接受 NaN。 */
	if (fc_circle->radius < 0.0)
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
				 errmsg("invalid input syntax for type %s: \"%s\"",
						"circle", fc_str)));

	while (fc_depth > 0)
	{
		if ((*fc_s == RDELIM) || ((*fc_s == RDELIM_C) && (fc_depth == 1)))
		{
			fc_depth--;
			fc_s++;
			while (isspace((unsigned char) *fc_s))
				fc_s++;
		}
		else
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
					 errmsg("invalid input syntax for type %s: \"%s\"",
							"circle", fc_str)));
	}

	if (*fc_s != '\0')
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
				 errmsg("invalid input syntax for type %s: \"%s\"",
						"circle", fc_str)));

	PG_RETURN_CIRCLE_P(fc_circle);
}

/*		circle_out		-		将圆转换为外部形式。
 */
Datum circle_out(PG_FUNCTION_ARGS)
{
	CIRCLE	   *fc_circle = PG_GETARG_CIRCLE_P(0);
	StringInfoData fc_str;

	initStringInfo(&fc_str);

	appendStringInfoChar(&fc_str, LDELIM_C);
	appendStringInfoChar(&fc_str, LDELIM);
	fc_pair_encode(fc_circle->center.x, fc_circle->center.y, &fc_str);
	appendStringInfoChar(&fc_str, RDELIM);
	appendStringInfoChar(&fc_str, DELIM);
	fc_single_encode(fc_circle->radius, &fc_str);
	appendStringInfoChar(&fc_str, RDELIM_C);

	PG_RETURN_CSTRING(fc_str.data);
}

/*
 *		circle_recv			- 将外部二进制格式转换为圆
 */
Datum circle_recv(PG_FUNCTION_ARGS)
{
	StringInfo	fc_buf = (StringInfo) PG_GETARG_POINTER(0);
	CIRCLE	   *fc_circle;

	fc_circle = (CIRCLE *) palloc(sizeof(CIRCLE));

	fc_circle->center.x = pq_getmsgfloat8(fc_buf);
	fc_circle->center.y = pq_getmsgfloat8(fc_buf);
	fc_circle->radius = pq_getmsgfloat8(fc_buf);

	/* 我们必须接受 NaN。 */
	if (fc_circle->radius < 0.0)
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
				 errmsg("invalid radius in external \"circle\" value")));

	PG_RETURN_CIRCLE_P(fc_circle);
}

/*
 *		circle_send			- 将圆转换为二进制格式
 */
Datum circle_send(PG_FUNCTION_ARGS)
{
	CIRCLE	   *fc_circle = PG_GETARG_CIRCLE_P(0);
	StringInfoData fc_buf;

	pq_begintypsend(&fc_buf);
	pq_sendfloat8(&fc_buf, fc_circle->center.x);
	pq_sendfloat8(&fc_buf, fc_circle->center.y);
	pq_sendfloat8(&fc_buf, fc_circle->radius);
	PG_RETURN_BYTEA_P(pq_endtypsend(&fc_buf));
}


/*----------------------------------------------------------
 *	圆的关系操作符。
 *		<, >, <=, >= 和 == 基于圆的面积。
 *---------------------------------------------------------*/

/*		圆是否相同？
 *
 * 我们认为 NaNs 值彼此相等，以使这些圆能够被找到。
 */
Datum circle_same(PG_FUNCTION_ARGS)
{
	CIRCLE	   *fc_circle1 = PG_GETARG_CIRCLE_P(0);
	CIRCLE	   *fc_circle2 = PG_GETARG_CIRCLE_P(1);

	PG_RETURN_BOOL(((isnan(fc_circle1->radius) && isnan(fc_circle2->radius)) ||
					FPeq(fc_circle1->radius, fc_circle2->radius)) &&
				   fc_point_eq_point(&fc_circle1->center, &fc_circle2->center));
}

/*		circle_overlap	-		圆 1 是否与圆 2 重叠？
 */
Datum circle_overlap(PG_FUNCTION_ARGS)
{
	CIRCLE	   *fc_circle1 = PG_GETARG_CIRCLE_P(0);
	CIRCLE	   *fc_circle2 = PG_GETARG_CIRCLE_P(1);

	PG_RETURN_BOOL(FPle(fc_point_dt(&fc_circle1->center, &fc_circle2->center),
						float8_pl(fc_circle1->radius, fc_circle2->radius)));
}

/*		circle_overleft -		圆 1 的右边缘是否在或左于
 *								圆 2 的右边缘？
 */
Datum circle_overleft(PG_FUNCTION_ARGS)
{
	CIRCLE	   *fc_circle1 = PG_GETARG_CIRCLE_P(0);
	CIRCLE	   *fc_circle2 = PG_GETARG_CIRCLE_P(1);

	PG_RETURN_BOOL(FPle(float8_pl(fc_circle1->center.x, fc_circle1->radius),
						float8_pl(fc_circle2->center.x, fc_circle2->radius)));
}

/*		circle_left		-		圆 1 是否严格在圆 2 的左侧？
 */
Datum circle_left(PG_FUNCTION_ARGS)
{
	CIRCLE	   *fc_circle1 = PG_GETARG_CIRCLE_P(0);
	CIRCLE	   *fc_circle2 = PG_GETARG_CIRCLE_P(1);

	PG_RETURN_BOOL(FPlt(float8_pl(fc_circle1->center.x, fc_circle1->radius),
						float8_mi(fc_circle2->center.x, fc_circle2->radius)));
}

/*		circle_right	-		圆 1 是否严格在圆 2 的右侧？
 */
Datum circle_right(PG_FUNCTION_ARGS)
{
	CIRCLE	   *fc_circle1 = PG_GETARG_CIRCLE_P(0);
	CIRCLE	   *fc_circle2 = PG_GETARG_CIRCLE_P(1);

	PG_RETURN_BOOL(FPgt(float8_mi(fc_circle1->center.x, fc_circle1->radius),
						float8_pl(fc_circle2->center.x, fc_circle2->radius)));
}

/*		circle_overright	-	圆 1 的左边缘是否在或右于
 *								圆 2 的左边缘？
 */
Datum circle_overright(PG_FUNCTION_ARGS)
{
	CIRCLE	   *fc_circle1 = PG_GETARG_CIRCLE_P(0);
	CIRCLE	   *fc_circle2 = PG_GETARG_CIRCLE_P(1);

	PG_RETURN_BOOL(FPge(float8_mi(fc_circle1->center.x, fc_circle1->radius),
						float8_mi(fc_circle2->center.x, fc_circle2->radius)));
}

/*		circle_contained		-	圆 1 是否被圆 2 包含？
 */
Datum circle_contained(PG_FUNCTION_ARGS)
{
	CIRCLE	   *fc_circle1 = PG_GETARG_CIRCLE_P(0);
	CIRCLE	   *fc_circle2 = PG_GETARG_CIRCLE_P(1);

	PG_RETURN_BOOL(FPle(fc_point_dt(&fc_circle1->center, &fc_circle2->center),
						float8_mi(fc_circle2->radius, fc_circle1->radius)));
}

/*		circle_contain	-		圆1 是否包含 圆2?
 */
Datum circle_contain(PG_FUNCTION_ARGS)
{
	CIRCLE	   *fc_circle1 = PG_GETARG_CIRCLE_P(0);
	CIRCLE	   *fc_circle2 = PG_GETARG_CIRCLE_P(1);

	PG_RETURN_BOOL(FPle(fc_point_dt(&fc_circle1->center, &fc_circle2->center),
						float8_mi(fc_circle1->radius, fc_circle2->radius)));
}


/*		circle_below		-		圆1 是否严格位于 圆2 之下?
 */
Datum circle_below(PG_FUNCTION_ARGS)
{
	CIRCLE	   *fc_circle1 = PG_GETARG_CIRCLE_P(0);
	CIRCLE	   *fc_circle2 = PG_GETARG_CIRCLE_P(1);

	PG_RETURN_BOOL(FPlt(float8_pl(fc_circle1->center.y, fc_circle1->radius),
						float8_mi(fc_circle2->center.y, fc_circle2->radius)));
}

/*		circle_above	-		圆1 是否严格位于 圆2 之上?
 */
Datum circle_above(PG_FUNCTION_ARGS)
{
	CIRCLE	   *fc_circle1 = PG_GETARG_CIRCLE_P(0);
	CIRCLE	   *fc_circle2 = PG_GETARG_CIRCLE_P(1);

	PG_RETURN_BOOL(FPgt(float8_mi(fc_circle1->center.y, fc_circle1->radius),
						float8_pl(fc_circle2->center.y, fc_circle2->radius)));
}

/*		circle_overbelow -		圆1 的上边缘是否位于或低于
 *								圆2 的上边缘?
 */
Datum circle_overbelow(PG_FUNCTION_ARGS)
{
	CIRCLE	   *fc_circle1 = PG_GETARG_CIRCLE_P(0);
	CIRCLE	   *fc_circle2 = PG_GETARG_CIRCLE_P(1);

	PG_RETURN_BOOL(FPle(float8_pl(fc_circle1->center.y, fc_circle1->radius),
						float8_pl(fc_circle2->center.y, fc_circle2->radius)));
}

/*		circle_overabove	-	圆1 的下边缘是否位于或高于
 *								圆2 的下边缘?
 */
Datum circle_overabove(PG_FUNCTION_ARGS)
{
	CIRCLE	   *fc_circle1 = PG_GETARG_CIRCLE_P(0);
	CIRCLE	   *fc_circle2 = PG_GETARG_CIRCLE_P(1);

	PG_RETURN_BOOL(FPge(float8_mi(fc_circle1->center.y, fc_circle1->radius),
						float8_mi(fc_circle2->center.y, fc_circle2->radius)));
}


/*		circle_relop	-		area(圆1) 与 area(圆2) 是否在
 *								我们的精度约束内?
 */
Datum circle_eq(PG_FUNCTION_ARGS)
{
	CIRCLE	   *fc_circle1 = PG_GETARG_CIRCLE_P(0);
	CIRCLE	   *fc_circle2 = PG_GETARG_CIRCLE_P(1);

	PG_RETURN_BOOL(FPeq(fc_circle_ar(fc_circle1), fc_circle_ar(fc_circle2)));
}

Datum circle_ne(PG_FUNCTION_ARGS)
{
	CIRCLE	   *fc_circle1 = PG_GETARG_CIRCLE_P(0);
	CIRCLE	   *fc_circle2 = PG_GETARG_CIRCLE_P(1);

	PG_RETURN_BOOL(FPne(fc_circle_ar(fc_circle1), fc_circle_ar(fc_circle2)));
}

Datum circle_lt(PG_FUNCTION_ARGS)
{
	CIRCLE	   *fc_circle1 = PG_GETARG_CIRCLE_P(0);
	CIRCLE	   *fc_circle2 = PG_GETARG_CIRCLE_P(1);

	PG_RETURN_BOOL(FPlt(fc_circle_ar(fc_circle1), fc_circle_ar(fc_circle2)));
}

Datum circle_gt(PG_FUNCTION_ARGS)
{
	CIRCLE	   *fc_circle1 = PG_GETARG_CIRCLE_P(0);
	CIRCLE	   *fc_circle2 = PG_GETARG_CIRCLE_P(1);

	PG_RETURN_BOOL(FPgt(fc_circle_ar(fc_circle1), fc_circle_ar(fc_circle2)));
}

Datum circle_le(PG_FUNCTION_ARGS)
{
	CIRCLE	   *fc_circle1 = PG_GETARG_CIRCLE_P(0);
	CIRCLE	   *fc_circle2 = PG_GETARG_CIRCLE_P(1);

	PG_RETURN_BOOL(FPle(fc_circle_ar(fc_circle1), fc_circle_ar(fc_circle2)));
}

Datum circle_ge(PG_FUNCTION_ARGS)
{
	CIRCLE	   *fc_circle1 = PG_GETARG_CIRCLE_P(0);
	CIRCLE	   *fc_circle2 = PG_GETARG_CIRCLE_P(1);

	PG_RETURN_BOOL(FPge(fc_circle_ar(fc_circle1), fc_circle_ar(fc_circle2)));
}


/*----------------------------------------------------------
 *	"算术"操作符 对于圆形。
 *---------------------------------------------------------*/

/* circle_add_pt()
 * 平移操作符。
 */
Datum circle_add_pt(PG_FUNCTION_ARGS)
{
	CIRCLE	   *fc_circle = PG_GETARG_CIRCLE_P(0);
	Point	   *fc_point = PG_GETARG_POINT_P(1);
	CIRCLE	   *fc_result;

	fc_result = (CIRCLE *) palloc(sizeof(CIRCLE));

	fc_point_add_point(&fc_result->center, &fc_circle->center, fc_point);
	fc_result->radius = fc_circle->radius;

	PG_RETURN_CIRCLE_P(fc_result);
}

Datum circle_sub_pt(PG_FUNCTION_ARGS)
{
	CIRCLE	   *fc_circle = PG_GETARG_CIRCLE_P(0);
	Point	   *fc_point = PG_GETARG_POINT_P(1);
	CIRCLE	   *fc_result;

	fc_result = (CIRCLE *) palloc(sizeof(CIRCLE));

	fc_point_sub_point(&fc_result->center, &fc_circle->center, fc_point);
	fc_result->radius = fc_circle->radius;

	PG_RETURN_CIRCLE_P(fc_result);
}


/* circle_mul_pt()
 * 旋转和缩放操作符。
 */
Datum circle_mul_pt(PG_FUNCTION_ARGS)
{
	CIRCLE	   *fc_circle = PG_GETARG_CIRCLE_P(0);
	Point	   *fc_point = PG_GETARG_POINT_P(1);
	CIRCLE	   *fc_result;

	fc_result = (CIRCLE *) palloc(sizeof(CIRCLE));

	fc_point_mul_point(&fc_result->center, &fc_circle->center, fc_point);
	fc_result->radius = float8_mul(fc_circle->radius, HYPOT(fc_point->x, fc_point->y));

	PG_RETURN_CIRCLE_P(fc_result);
}

Datum circle_div_pt(PG_FUNCTION_ARGS)
{
	CIRCLE	   *fc_circle = PG_GETARG_CIRCLE_P(0);
	Point	   *fc_point = PG_GETARG_POINT_P(1);
	CIRCLE	   *fc_result;

	fc_result = (CIRCLE *) palloc(sizeof(CIRCLE));

	fc_point_div_point(&fc_result->center, &fc_circle->center, fc_point);
	fc_result->radius = float8_div(fc_circle->radius, HYPOT(fc_point->x, fc_point->y));

	PG_RETURN_CIRCLE_P(fc_result);
}


/*		circle_area		-		返回圆的面积。
 */
Datum circle_area(PG_FUNCTION_ARGS)
{
	CIRCLE	   *fc_circle = PG_GETARG_CIRCLE_P(0);

	PG_RETURN_FLOAT8(fc_circle_ar(fc_circle));
}


/*		circle_diameter -		返回圆的直径。
 */
Datum circle_diameter(PG_FUNCTION_ARGS)
{
	CIRCLE	   *fc_circle = PG_GETARG_CIRCLE_P(0);

	PG_RETURN_FLOAT8(float8_mul(fc_circle->radius, 2.0));
}


/*		circle_radius	-		返回圆的半径。
 */
Datum circle_radius(PG_FUNCTION_ARGS)
{
	CIRCLE	   *fc_circle = PG_GETARG_CIRCLE_P(0);

	PG_RETURN_FLOAT8(fc_circle->radius);
}


/*		circle_distance -		返回两个圆之间的距离。
 */
Datum circle_distance(PG_FUNCTION_ARGS)
{
	CIRCLE	   *fc_circle1 = PG_GETARG_CIRCLE_P(0);
	CIRCLE	   *fc_circle2 = PG_GETARG_CIRCLE_P(1);
	float8		fc_result;

	fc_result = float8_mi(fc_point_dt(&fc_circle1->center, &fc_circle2->center),
					   float8_pl(fc_circle1->radius, fc_circle2->radius));
	if (fc_result < 0.0)
		fc_result = 0.0;

	PG_RETURN_FLOAT8(fc_result);
}


Datum circle_contain_pt(PG_FUNCTION_ARGS)
{
	CIRCLE	   *fc_circle = PG_GETARG_CIRCLE_P(0);
	Point	   *fc_point = PG_GETARG_POINT_P(1);
	float8		fc_d;

	fc_d = fc_point_dt(&fc_circle->center, fc_point);
	PG_RETURN_BOOL(fc_d <= fc_circle->radius);
}


Datum pt_contained_circle(PG_FUNCTION_ARGS)
{
	Point	   *fc_point = PG_GETARG_POINT_P(0);
	CIRCLE	   *fc_circle = PG_GETARG_CIRCLE_P(1);
	float8		fc_d;

	fc_d = fc_point_dt(&fc_circle->center, fc_point);
	PG_RETURN_BOOL(fc_d <= fc_circle->radius);
}


/*		dist_pc -		返回一个点与一个圆之间的距离。
 */
Datum dist_pc(PG_FUNCTION_ARGS)
{
	Point	   *fc_point = PG_GETARG_POINT_P(0);
	CIRCLE	   *fc_circle = PG_GETARG_CIRCLE_P(1);
	float8		fc_result;

	fc_result = float8_mi(fc_point_dt(fc_point, &fc_circle->center),
					   fc_circle->radius);
	if (fc_result < 0.0)
		fc_result = 0.0;

	PG_RETURN_FLOAT8(fc_result);
}

/*
 * 圆到点的距离
 */
Datum dist_cpoint(PG_FUNCTION_ARGS)
{
	CIRCLE	   *fc_circle = PG_GETARG_CIRCLE_P(0);
	Point	   *fc_point = PG_GETARG_POINT_P(1);
	float8		fc_result;

	fc_result = float8_mi(fc_point_dt(fc_point, &fc_circle->center), fc_circle->radius);
	if (fc_result < 0.0)
		fc_result = 0.0;

	PG_RETURN_FLOAT8(fc_result);
}

/*		circle_center	-		返回圆的中心点。
 */
Datum circle_center(PG_FUNCTION_ARGS)
{
	CIRCLE	   *fc_circle = PG_GETARG_CIRCLE_P(0);
	Point	   *fc_result;

	fc_result = (Point *) palloc(sizeof(Point));
	fc_result->x = fc_circle->center.x;
	fc_result->y = fc_circle->center.y;

	PG_RETURN_POINT_P(fc_result);
}


/*		circle_ar		-		返回圆的面积。
 */
static float8 fc_circle_ar(CIRCLE *fc_circle)
{
	return float8_mul(float8_mul(fc_circle->radius, fc_circle->radius), M_PI);
}


/*----------------------------------------------------------
 *	转换操作符。
 *---------------------------------------------------------*/

Datum cr_circle(PG_FUNCTION_ARGS)
{
	Point	   *fc_center = PG_GETARG_POINT_P(0);
	float8		fc_radius = PG_GETARG_FLOAT8(1);
	CIRCLE	   *fc_result;

	fc_result = (CIRCLE *) palloc(sizeof(CIRCLE));

	fc_result->center.x = fc_center->x;
	fc_result->center.y = fc_center->y;
	fc_result->radius = fc_radius;

	PG_RETURN_CIRCLE_P(fc_result);
}

Datum circle_box(PG_FUNCTION_ARGS)
{
	CIRCLE	   *fc_circle = PG_GETARG_CIRCLE_P(0);
	BOX		   *fc_box;
	float8		fc_delta;

	fc_box = (BOX *) palloc(sizeof(BOX));

	fc_delta = float8_div(fc_circle->radius, sqrt(2.0));

	fc_box->high.x = float8_pl(fc_circle->center.x, fc_delta);
	fc_box->low.x = float8_mi(fc_circle->center.x, fc_delta);
	fc_box->high.y = float8_pl(fc_circle->center.y, fc_delta);
	fc_box->low.y = float8_mi(fc_circle->center.y, fc_delta);

	PG_RETURN_BOX_P(fc_box);
}

/* box_circle()
 * 将一个矩形转换为一个圆。
 */
Datum box_circle(PG_FUNCTION_ARGS)
{
	BOX		   *fc_box = PG_GETARG_BOX_P(0);
	CIRCLE	   *fc_circle;

	fc_circle = (CIRCLE *) palloc(sizeof(CIRCLE));

	fc_circle->center.x = float8_div(float8_pl(fc_box->high.x, fc_box->low.x), 2.0);
	fc_circle->center.y = float8_div(float8_pl(fc_box->high.y, fc_box->low.y), 2.0);

	fc_circle->radius = fc_point_dt(&fc_circle->center, &fc_box->high);

	PG_RETURN_CIRCLE_P(fc_circle);
}


Datum circle_poly(PG_FUNCTION_ARGS)
{
	int32		fc_npts = PG_GETARG_INT32(0);
	CIRCLE	   *fc_circle = PG_GETARG_CIRCLE_P(1);
	POLYGON    *fc_poly;
	int			fc_base_size,
				fc_size;
	int			fc_i;
	float8		fc_angle;
	float8		fc_anglestep;

	if (FPzero(fc_circle->radius))
		ereport(ERROR,
				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
				 errmsg("cannot convert circle with radius zero to polygon")));

	if (fc_npts < 2)
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
				 errmsg("must request at least 2 points")));

	fc_base_size = sizeof(fc_poly->p[0]) * fc_npts;
	fc_size = offsetof(POLYGON, p) + fc_base_size;

	/* 检查整数溢出 */
	if (fc_base_size / fc_npts != sizeof(fc_poly->p[0]) || fc_size <= fc_base_size)
		ereport(ERROR,
				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
				 errmsg("too many points requested")));

	fc_poly = (POLYGON *) palloc0(fc_size);	/* 清除任何孔洞 */
	SET_VARSIZE(fc_poly, fc_size);
	fc_poly->npts = fc_npts;

	fc_anglestep = float8_div(2.0 * M_PI, fc_npts);

	for (fc_i = 0; fc_i < fc_npts; fc_i++)
	{
		fc_angle = float8_mul(fc_anglestep, fc_i);

		fc_poly->p[fc_i].x = float8_mi(fc_circle->center.x,
								 float8_mul(fc_circle->radius, cos(fc_angle)));
		fc_poly->p[fc_i].y = float8_pl(fc_circle->center.y,
								 float8_mul(fc_circle->radius, sin(fc_angle)));
	}

	fc_make_bound_box(fc_poly);

	PG_RETURN_POLYGON_P(fc_poly);
}

/*
 * 将多边形转换为圆
 *
 * 结果必须预分配。
 *
 * XXX 此算法应使用线段的加权平均值，而不是点的简单平均值 - tgl 97/01/21。
 */
static void fc_poly_to_circle(CIRCLE *fc_result, POLYGON *fc_poly)
{
	int			fc_i;

	Assert(fc_poly->npts > 0);

	fc_result->center.x = 0;
	fc_result->center.y = 0;
	fc_result->radius = 0;

	for (fc_i = 0; fc_i < fc_poly->npts; fc_i++)
		fc_point_add_point(&fc_result->center, &fc_result->center, &fc_poly->p[fc_i]);
	fc_result->center.x = float8_div(fc_result->center.x, fc_poly->npts);
	fc_result->center.y = float8_div(fc_result->center.y, fc_poly->npts);

	for (fc_i = 0; fc_i < fc_poly->npts; fc_i++)
		fc_result->radius = float8_pl(fc_result->radius,
								   fc_point_dt(&fc_poly->p[fc_i], &fc_result->center));
	fc_result->radius = float8_div(fc_result->radius, fc_poly->npts);
}

Datum poly_circle(PG_FUNCTION_ARGS)
{
	POLYGON    *fc_poly = PG_GETARG_POLYGON_P(0);
	CIRCLE	   *fc_result;

	fc_result = (CIRCLE *) palloc(sizeof(CIRCLE));

	fc_poly_to_circle(fc_result, fc_poly);

	PG_RETURN_CIRCLE_P(fc_result);
}


/***********************************************************************
 **
 **		多种类型的私有例程。
 **
 ***********************************************************************/

/*
 *	测试点是否在多边形内，返回1/0，或者如果点在多边形上则返回2。
 *	代码经过调整但未从 WN: A 服务器的基于整数的例程中复制
 *	HTTP版本1.15.1，文件 wn/image.c
 *	http://hopf.math.northwestern.edu/index.html
 *	算法描述:  http://www.linuxjournal.com/article/2197
 *							   http://www.linuxjournal.com/article/2029
 */

#define POINT_ON_POLYGON INT_MAX

static int fc_point_inside(Point *fc_p, int fc_npts, Point *fc_plist)
{
	float8		fc_x0,
				fc_y0;
	float8		fc_prev_x,
				fc_prev_y;
	int			fc_i = 0;
	float8		fc_x,
				fc_y;
	int			fc_cross,
				fc_total_cross = 0;

	Assert(fc_npts > 0);

	/* 计算相对于单一点的第一个多边形点 */
	fc_x0 = float8_mi(fc_plist[0].x, fc_p->x);
	fc_y0 = float8_mi(fc_plist[0].y, fc_p->y);

	fc_prev_x = fc_x0;
	fc_prev_y = fc_y0;
	/* 遍历多边形点并汇总 total_cross */
	for (fc_i = 1; fc_i < fc_npts; fc_i++)
	{
		/* 计算相对于单一点的下一个多边形点 */
		fc_x = float8_mi(fc_plist[fc_i].x, fc_p->x);
		fc_y = float8_mi(fc_plist[fc_i].y, fc_p->y);

		/* 计算当前点到前一个点的交叉 */
		if ((fc_cross = fc_lseg_crossing(fc_x, fc_y, fc_prev_x, fc_prev_y)) == POINT_ON_POLYGON)
			return 2;
		fc_total_cross += fc_cross;

		fc_prev_x = fc_x;
		fc_prev_y = fc_y;
	}

	/* 现在处理第一个点 */
	if ((fc_cross = fc_lseg_crossing(fc_x0, fc_y0, fc_prev_x, fc_prev_y)) == POINT_ON_POLYGON)
		return 2;
	fc_total_cross += fc_cross;

	if (fc_total_cross != 0)
		return 1;
	return 0;
}


/* lseg_crossing()
 * 如果线段在正X轴上以 +/- 方向交叉，则返回 +/-2。
 * 如果一个点在正X轴上，则返回 +/-1。
 * 如果两个点都在正X轴上，或者没有交叉，则返回0。
 * 如果线段包含 (0,0)，则返回 POINT_ON_POLYGON。
 * 哇，这真是一个令人困惑的 API，但它在上面被使用，当求和时，
 * 可以告诉我们一个点是否在多边形内。
 */

static int fc_lseg_crossing(float8 fc_x, float8 fc_y, float8 fc_prev_x, float8 fc_prev_y)
{
	float8		fc_z;
	int			fc_y_sign;

	if (FPzero(fc_y))
	{							/* y == 0，位于 X 轴上 */
		if (FPzero(fc_x))			/* (x,y) 是否为 (0,0)? */
			return POINT_ON_POLYGON;
		else if (FPgt(fc_x, 0))
		{						/* x > 0 */
			if (FPzero(fc_prev_y)) /* y 和 prev_y 为零 */
				/* prev_x > 0? */
				return FPgt(fc_prev_x, 0.0) ? 0 : POINT_ON_POLYGON;
			return FPlt(fc_prev_y, 0.0) ? 1 : -1;
		}
		else
		{						/* x < 0, x 不在正 X 轴上 */
			if (FPzero(fc_prev_y))
				/* prev_x < 0? */
				return FPlt(fc_prev_x, 0.0) ? 0 : POINT_ON_POLYGON;
			return 0;
		}
	}
	else
	{							/* y != 0 */
		/* 计算 y 交叉方向从前一个点 */
		fc_y_sign = FPgt(fc_y, 0.0) ? 1 : -1;

		if (FPzero(fc_prev_y))
			/* 前一个点在 X 轴上，因此新点要么在外部要么在内部 */
			return FPlt(fc_prev_x, 0.0) ? 0 : fc_y_sign;
		else if ((fc_y_sign < 0 && FPlt(fc_prev_y, 0.0)) ||
				 (fc_y_sign > 0 && FPgt(fc_prev_y, 0.0)))
			/* 都在 X 轴的上方或下方 */
			return 0;			/* 同号 */
		else
		{						/* y 和 prev_y 交叉 X 轴 */
			if (FPge(fc_x, 0.0) && FPgt(fc_prev_x, 0.0))
				/* 都为非负数，因此交叉正 X 轴 */
				return 2 * fc_y_sign;
			if (FPlt(fc_x, 0.0) && FPle(fc_prev_x, 0.0))
				/* 都为非正数，因此不交叉正 X 轴 */
				return 0;

			/* x 和 y 交叉轴，见上面的 URL point_inside() */
			fc_z = float8_mi(float8_mul(float8_mi(fc_x, fc_prev_x), fc_y),
						  float8_mul(float8_mi(fc_y, fc_prev_y), fc_x));
			if (FPzero(fc_z))
				return POINT_ON_POLYGON;
			if ((fc_y_sign < 0 && FPlt(fc_z, 0.0)) ||
				(fc_y_sign > 0 && FPgt(fc_z, 0.0)))
				return 0;
			return 2 * fc_y_sign;
		}
	}
}


static bool fc_plist_same(int fc_npts, Point *fc_p1, Point *fc_p2)
{
	int			fc_i,
				fc_ii,
				fc_j;

	/* 查找第一个点的匹配 */
	for (fc_i = 0; fc_i < fc_npts; fc_i++)
	{
		if (fc_point_eq_point(&fc_p2[fc_i], &fc_p1[0]))
		{

			/* 找到匹配了吗？然后向前查找剩余的点 */
			for (fc_ii = 1, fc_j = fc_i + 1; fc_ii < fc_npts; fc_ii++, fc_j++)
			{
				if (fc_j >= fc_npts)
					fc_j = 0;
				if (!fc_point_eq_point(&fc_p2[fc_j], &fc_p1[fc_ii]))
					break;
			}
			if (fc_ii == fc_npts)
				return true;

			/* 向前未找到匹配？那么向后查找 */
			for (fc_ii = 1, fc_j = fc_i - 1; fc_ii < fc_npts; fc_ii++, fc_j--)
			{
				if (fc_j < 0)
					fc_j = (fc_npts - 1);
				if (!fc_point_eq_point(&fc_p2[fc_j], &fc_p1[fc_ii]))
					break;
			}
			if (fc_ii == fc_npts)
				return true;
		}
	}

	return false;
}


/*-------------------------------------------------------------------------
 * 确定斜边。
 *
 * 如果需要，将 x 和 y 交换，使 x 成为更大的数。传统的公式
 * x^2+y^2 被重新排列，以将 x 提取在 sqrt 之外。这允许
 * 计算显著更大值的斜边，并且具有比使用简单公式更高的精度。
 * 特别是，除非最终结果超出范围，否则这不会溢出。
 *
 * sqrt( x^2 + y^2 ) = sqrt( x^2( 1 + y^2/x^2) )
 *					 = x * sqrt( 1 + y^2/x^2 )
 *					 = x * sqrt( 1 + y/x * y/x )
 *
 * 预计该例程最终会被 C99 hypot() 函数取代。
 *
 * 此实现遵循 IEEE 标准 1003.1 和 GLIBC，因为 hypot(inf,nan)
 * 的结果为 INF，而不是 NAN。
 *-----------------------------------------------------------------------
 */
float8 pg_hypot(float8 fc_x, float8 fc_y)
{
	float8		fc_yx,
				fc_result;

	/* 正确处理 INF 和 NaN */
	if (isinf(fc_x) || isinf(fc_y))
		return get_float8_infinity();

	if (isnan(fc_x) || isnan(fc_y))
		return get_float8_nan();

	/* 否则，去掉任何负号 */
	fc_x = fabs(fc_x);
	fc_y = fabs(fc_y);

	/* 如有需要，交换 x 和 y 使 x 成为更大的数 */
	if (fc_x < fc_y)
	{
		float8		fc_temp = fc_x;

		fc_x = fc_y;
		fc_y = fc_temp;
	}

	/*
	 * 如果 y 为零，则斜边为 x。此测试在这种情况下节省了一些周期，
	 * 更重要的是，它还保护了除以零的错误，因为现在 x >= y。
	 */
	if (fc_y == 0.0)
		return fc_x;

	/* 确定斜边 */
	fc_yx = fc_y / fc_x;
	fc_result = fc_x * sqrt(1.0 + (fc_yx * fc_yx));

	if (unlikely(isinf(fc_result)))
		float_overflow_error();
	if (unlikely(fc_result == 0.0))
		float_underflow_error();

	return fc_result;
}
